/**
 * Synor Compute SDK - C Implementation
 */

#include "../include/synor_compute.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <curl/curl.h>

/* ============ Internal Structures ============ */

struct synor_client {
    char* api_key;
    char* base_url;
    synor_processor_t default_processor;
    synor_precision_t default_precision;
    synor_priority_t default_priority;
    int timeout_ms;
    bool debug;
    bool closed;
    CURL* curl;
};

typedef struct {
    char* data;
    size_t size;
} response_buffer_t;

/* ============ Internal Functions ============ */

static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
    size_t realsize = size * nmemb;
    response_buffer_t* buf = (response_buffer_t*)userp;

    char* ptr = realloc(buf->data, buf->size + realsize + 1);
    if (!ptr) return 0;

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

    return realsize;
}

static char* synor_strdup(const char* str) {
    if (!str) return NULL;
    size_t len = strlen(str) + 1;
    char* copy = malloc(len);
    if (copy) memcpy(copy, str, len);
    return copy;
}

static size_t compute_tensor_size(const int* shape, int ndim) {
    size_t size = 1;
    for (int i = 0; i < ndim; i++) {
        size *= shape[i];
    }
    return size;
}

/* ============ Client Functions ============ */

void synor_config_default(synor_config_t* config) {
    config->api_key = NULL;
    config->base_url = "https://api.synor.io/compute/v1";
    config->default_processor = SYNOR_PROCESSOR_AUTO;
    config->default_precision = SYNOR_PRECISION_FP32;
    config->default_priority = SYNOR_PRIORITY_NORMAL;
    config->timeout_ms = 30000;
    config->debug = false;
}

synor_client_t* synor_create(const char* api_key) {
    synor_config_t config;
    synor_config_default(&config);
    config.api_key = api_key;
    return synor_create_with_config(&config);
}

synor_client_t* synor_create_with_config(const synor_config_t* config) {
    if (!config || !config->api_key) return NULL;

    synor_client_t* client = calloc(1, sizeof(synor_client_t));
    if (!client) return NULL;

    client->api_key = synor_strdup(config->api_key);
    client->base_url = synor_strdup(config->base_url ? config->base_url : "https://api.synor.io/compute/v1");
    client->default_processor = config->default_processor;
    client->default_precision = config->default_precision;
    client->default_priority = config->default_priority;
    client->timeout_ms = config->timeout_ms;
    client->debug = config->debug;
    client->closed = false;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    client->curl = curl_easy_init();

    return client;
}

void synor_destroy(synor_client_t* client) {
    if (!client) return;

    client->closed = true;

    if (client->curl) {
        curl_easy_cleanup(client->curl);
    }
    curl_global_cleanup();

    free(client->api_key);
    free(client->base_url);
    free(client);
}

/* ============ Tensor Functions ============ */

synor_tensor_t* synor_tensor_create(
    const int* shape,
    int ndim,
    const double* data,
    synor_precision_t dtype
) {
    synor_tensor_t* tensor = calloc(1, sizeof(synor_tensor_t));
    if (!tensor) return NULL;

    tensor->ndim = ndim;
    tensor->dtype = dtype;
    tensor->size = compute_tensor_size(shape, ndim);

    tensor->shape = malloc(ndim * sizeof(int));
    if (!tensor->shape) {
        free(tensor);
        return NULL;
    }
    memcpy(tensor->shape, shape, ndim * sizeof(int));

    tensor->data = malloc(tensor->size * sizeof(double));
    if (!tensor->data) {
        free(tensor->shape);
        free(tensor);
        return NULL;
    }

    if (data) {
        memcpy(tensor->data, data, tensor->size * sizeof(double));
    }

    return tensor;
}

synor_tensor_t* synor_tensor_zeros(const int* shape, int ndim) {
    synor_tensor_t* tensor = synor_tensor_create(shape, ndim, NULL, SYNOR_PRECISION_FP32);
    if (tensor) {
        memset(tensor->data, 0, tensor->size * sizeof(double));
    }
    return tensor;
}

synor_tensor_t* synor_tensor_ones(const int* shape, int ndim) {
    synor_tensor_t* tensor = synor_tensor_create(shape, ndim, NULL, SYNOR_PRECISION_FP32);
    if (tensor) {
        for (size_t i = 0; i < tensor->size; i++) {
            tensor->data[i] = 1.0;
        }
    }
    return tensor;
}

synor_tensor_t* synor_tensor_rand(const int* shape, int ndim) {
    synor_tensor_t* tensor = synor_tensor_create(shape, ndim, NULL, SYNOR_PRECISION_FP32);
    if (tensor) {
        srand((unsigned int)time(NULL));
        for (size_t i = 0; i < tensor->size; i++) {
            tensor->data[i] = (double)rand() / RAND_MAX;
        }
    }
    return tensor;
}

synor_tensor_t* synor_tensor_randn(const int* shape, int ndim) {
    synor_tensor_t* tensor = synor_tensor_create(shape, ndim, NULL, SYNOR_PRECISION_FP32);
    if (tensor) {
        srand((unsigned int)time(NULL));
        for (size_t i = 0; i < tensor->size; i++) {
            // Box-Muller transform
            double u1 = (double)rand() / RAND_MAX;
            double u2 = (double)rand() / RAND_MAX;
            tensor->data[i] = sqrt(-2 * log(u1)) * cos(2 * M_PI * u2);
        }
    }
    return tensor;
}

synor_tensor_t* synor_tensor_eye(int n) {
    int shape[] = {n, n};
    synor_tensor_t* tensor = synor_tensor_zeros(shape, 2);
    if (tensor) {
        for (int i = 0; i < n; i++) {
            tensor->data[i * n + i] = 1.0;
        }
    }
    return tensor;
}

synor_tensor_t* synor_tensor_reshape(const synor_tensor_t* tensor, const int* new_shape, int new_ndim) {
    if (!tensor) return NULL;

    size_t new_size = compute_tensor_size(new_shape, new_ndim);
    if (new_size != tensor->size) return NULL;

    return synor_tensor_create(new_shape, new_ndim, tensor->data, tensor->dtype);
}

synor_tensor_t* synor_tensor_transpose(const synor_tensor_t* tensor) {
    if (!tensor || tensor->ndim != 2) return NULL;

    int rows = tensor->shape[0];
    int cols = tensor->shape[1];
    int new_shape[] = {cols, rows};

    synor_tensor_t* result = synor_tensor_create(new_shape, 2, NULL, tensor->dtype);
    if (!result) return NULL;

    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            result->data[j * rows + i] = tensor->data[i * cols + j];
        }
    }

    return result;
}

double synor_tensor_mean(const synor_tensor_t* tensor) {
    if (!tensor || tensor->size == 0) return 0.0;
    double sum = 0.0;
    for (size_t i = 0; i < tensor->size; i++) {
        sum += tensor->data[i];
    }
    return sum / tensor->size;
}

double synor_tensor_sum(const synor_tensor_t* tensor) {
    if (!tensor) return 0.0;
    double sum = 0.0;
    for (size_t i = 0; i < tensor->size; i++) {
        sum += tensor->data[i];
    }
    return sum;
}

double synor_tensor_std(const synor_tensor_t* tensor) {
    if (!tensor || tensor->size == 0) return 0.0;
    double mean = synor_tensor_mean(tensor);
    double sum_sq = 0.0;
    for (size_t i = 0; i < tensor->size; i++) {
        double diff = tensor->data[i] - mean;
        sum_sq += diff * diff;
    }
    return sqrt(sum_sq / tensor->size);
}

double synor_tensor_max(const synor_tensor_t* tensor) {
    if (!tensor || tensor->size == 0) return 0.0;
    double max = tensor->data[0];
    for (size_t i = 1; i < tensor->size; i++) {
        if (tensor->data[i] > max) max = tensor->data[i];
    }
    return max;
}

double synor_tensor_min(const synor_tensor_t* tensor) {
    if (!tensor || tensor->size == 0) return 0.0;
    double min = tensor->data[0];
    for (size_t i = 1; i < tensor->size; i++) {
        if (tensor->data[i] < min) min = tensor->data[i];
    }
    return min;
}

void synor_tensor_free(synor_tensor_t* tensor) {
    if (!tensor) return;
    free(tensor->shape);
    free(tensor->data);
    free(tensor);
}

/* ============ Memory Management ============ */

void synor_job_result_free(synor_job_result_t* result) {
    if (!result) return;
    free(result->job_id);
    free(result->result_string);
    free(result->error);
    synor_tensor_free(result->result_tensor);
    free(result);
}

void synor_model_info_free(synor_model_info_t* info) {
    if (!info) return;
    free(info->id);
    free(info->name);
    free(info->description);
    free(info->format);
    free(info->license);
    free(info->cid);
    free(info);
}

void synor_model_info_array_free(synor_model_info_t* models, size_t count) {
    if (!models) return;
    for (size_t i = 0; i < count; i++) {
        free(models[i].id);
        free(models[i].name);
        free(models[i].description);
        free(models[i].format);
        free(models[i].license);
        free(models[i].cid);
    }
    free(models);
}

void synor_pricing_info_array_free(synor_pricing_info_t* pricing, size_t count) {
    free(pricing);
}

void synor_usage_stats_free(synor_usage_stats_t* stats) {
    free(stats);
}

/* ============ Utility Functions ============ */

const char* synor_error_string(synor_error_t error) {
    switch (error) {
        case SYNOR_OK: return "OK";
        case SYNOR_ERROR_INVALID_ARGUMENT: return "Invalid argument";
        case SYNOR_ERROR_OUT_OF_MEMORY: return "Out of memory";
        case SYNOR_ERROR_NETWORK: return "Network error";
        case SYNOR_ERROR_API: return "API error";
        case SYNOR_ERROR_TIMEOUT: return "Timeout";
        case SYNOR_ERROR_CLIENT_CLOSED: return "Client closed";
        default: return "Unknown error";
    }
}

const char* synor_processor_string(synor_processor_t processor) {
    switch (processor) {
        case SYNOR_PROCESSOR_CPU: return "cpu";
        case SYNOR_PROCESSOR_GPU: return "gpu";
        case SYNOR_PROCESSOR_TPU: return "tpu";
        case SYNOR_PROCESSOR_NPU: return "npu";
        case SYNOR_PROCESSOR_LPU: return "lpu";
        case SYNOR_PROCESSOR_FPGA: return "fpga";
        case SYNOR_PROCESSOR_DSP: return "dsp";
        case SYNOR_PROCESSOR_WEBGPU: return "webgpu";
        case SYNOR_PROCESSOR_WASM: return "wasm";
        case SYNOR_PROCESSOR_AUTO: return "auto";
        default: return "unknown";
    }
}

const char* synor_precision_string(synor_precision_t precision) {
    switch (precision) {
        case SYNOR_PRECISION_FP64: return "fp64";
        case SYNOR_PRECISION_FP32: return "fp32";
        case SYNOR_PRECISION_FP16: return "fp16";
        case SYNOR_PRECISION_BF16: return "bf16";
        case SYNOR_PRECISION_INT8: return "int8";
        case SYNOR_PRECISION_INT4: return "int4";
        default: return "unknown";
    }
}

const char* synor_status_string(synor_status_t status) {
    switch (status) {
        case SYNOR_STATUS_PENDING: return "pending";
        case SYNOR_STATUS_QUEUED: return "queued";
        case SYNOR_STATUS_RUNNING: return "running";
        case SYNOR_STATUS_COMPLETED: return "completed";
        case SYNOR_STATUS_FAILED: return "failed";
        case SYNOR_STATUS_CANCELLED: return "cancelled";
        default: return "unknown";
    }
}

/* ============ API Functions (Stubs) ============ */

synor_job_result_t* synor_matmul(
    synor_client_t* client,
    const synor_tensor_t* a,
    const synor_tensor_t* b,
    const synor_matmul_options_t* options
) {
    if (!client || client->closed || !a || !b) return NULL;

    synor_job_result_t* result = calloc(1, sizeof(synor_job_result_t));
    if (!result) return NULL;

    // TODO: Implement HTTP call to API
    result->status = SYNOR_STATUS_COMPLETED;
    result->job_id = synor_strdup("job-placeholder");

    return result;
}

synor_job_result_t* synor_conv2d(
    synor_client_t* client,
    const synor_tensor_t* input,
    const synor_tensor_t* kernel,
    const synor_conv2d_options_t* options
) {
    if (!client || client->closed || !input || !kernel) return NULL;

    synor_job_result_t* result = calloc(1, sizeof(synor_job_result_t));
    if (!result) return NULL;

    result->status = SYNOR_STATUS_COMPLETED;
    result->job_id = synor_strdup("job-placeholder");

    return result;
}

synor_job_result_t* synor_attention(
    synor_client_t* client,
    const synor_tensor_t* query,
    const synor_tensor_t* key,
    const synor_tensor_t* value,
    const synor_attention_options_t* options
) {
    if (!client || client->closed || !query || !key || !value) return NULL;

    synor_job_result_t* result = calloc(1, sizeof(synor_job_result_t));
    if (!result) return NULL;

    result->status = SYNOR_STATUS_COMPLETED;
    result->job_id = synor_strdup("job-placeholder");

    return result;
}

synor_job_result_t* synor_inference(
    synor_client_t* client,
    const char* model,
    const char* prompt,
    const synor_inference_options_t* options
) {
    if (!client || client->closed || !model || !prompt) return NULL;

    synor_job_result_t* result = calloc(1, sizeof(synor_job_result_t));
    if (!result) return NULL;

    result->status = SYNOR_STATUS_COMPLETED;
    result->job_id = synor_strdup("job-placeholder");

    return result;
}

synor_error_t synor_inference_stream(
    synor_client_t* client,
    const char* model,
    const char* prompt,
    const synor_inference_options_t* options,
    synor_stream_callback_t callback,
    void* user_data
) {
    if (!client || client->closed) return SYNOR_ERROR_CLIENT_CLOSED;
    if (!model || !prompt || !callback) return SYNOR_ERROR_INVALID_ARGUMENT;

    // TODO: Implement streaming HTTP call
    return SYNOR_OK;
}

bool synor_health_check(synor_client_t* client) {
    if (!client || client->closed) return false;
    // TODO: Implement health check
    return true;
}

synor_error_t synor_list_models(
    synor_client_t* client,
    synor_model_category_t category,
    synor_model_info_t** models,
    size_t* count
) {
    if (!client || client->closed) return SYNOR_ERROR_CLIENT_CLOSED;
    if (!models || !count) return SYNOR_ERROR_INVALID_ARGUMENT;

    *models = NULL;
    *count = 0;
    // TODO: Implement model listing
    return SYNOR_OK;
}

synor_model_info_t* synor_get_model(synor_client_t* client, const char* model_id) {
    if (!client || client->closed || !model_id) return NULL;
    // TODO: Implement model fetching
    return NULL;
}

synor_error_t synor_search_models(
    synor_client_t* client,
    const char* query,
    synor_model_info_t** models,
    size_t* count
) {
    if (!client || client->closed) return SYNOR_ERROR_CLIENT_CLOSED;
    if (!query || !models || !count) return SYNOR_ERROR_INVALID_ARGUMENT;

    *models = NULL;
    *count = 0;
    // TODO: Implement model search
    return SYNOR_OK;
}

synor_error_t synor_get_pricing(
    synor_client_t* client,
    synor_pricing_info_t** pricing,
    size_t* count
) {
    if (!client || client->closed) return SYNOR_ERROR_CLIENT_CLOSED;
    if (!pricing || !count) return SYNOR_ERROR_INVALID_ARGUMENT;

    *pricing = NULL;
    *count = 0;
    // TODO: Implement pricing fetch
    return SYNOR_OK;
}

synor_usage_stats_t* synor_get_usage(synor_client_t* client) {
    if (!client || client->closed) return NULL;
    // TODO: Implement usage stats fetch
    return NULL;
}
