// SPDX-License-Identifier: GPL-2.0-only
/*
 *  ppm/src/util/http.c
 *
 *  Copyright (C) 2026 Project Tick
 */

#include "http.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <curl/curl.h>

#include "log.h"

static int g_initialised = 0;

ppm_error_t ppm_http_init(void) {
    if (g_initialised) return PPM_OK;
    if (curl_global_init(CURL_GLOBAL_DEFAULT) != 0) return PPM_ERR_INTERNAL;
    g_initialised = 1;
    return PPM_OK;
}

static size_t write_cb(void *buf, size_t sz, size_t nm, void *user) {
    FILE *f = user;
    return fwrite(buf, sz, nm, f);
}

ppm_error_t ppm_http_download(const char *url, const char *dest_path) {
    if (!url || !dest_path) return PPM_ERR_INVALID;
    ppm_error_t err = ppm_http_init();
    if (err) return err;

    /* Stage download into <dest>.part then rename atomically. */
    char *tmp;
    if (asprintf(&tmp, "%s.part", dest_path) < 0) return PPM_ERR_NOMEM;

    FILE *f = fopen(tmp, "wb");
    if (!f) { free(tmp); return PPM_ERR_IO; }

    CURL *c = curl_easy_init();
    if (!c) { fclose(f); unlink(tmp); free(tmp); return PPM_ERR_INTERNAL; }

    curl_easy_setopt(c, CURLOPT_URL, url);
    curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(c, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(c, CURLOPT_FAILONERROR, 1L);
    curl_easy_setopt(c, CURLOPT_NOPROGRESS, 1L);
    curl_easy_setopt(c, CURLOPT_USERAGENT, "ppm/0.1");
    curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 1024L);
    curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L);

    /* file://, http://, https:// only. */
    curl_easy_setopt(c, CURLOPT_PROTOCOLS_STR, "file,http,https");

    CURLcode rc = curl_easy_perform(c);
    long code = 0;
    curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &code);
    curl_easy_cleanup(c);

    if (fflush(f) != 0) { fclose(f); unlink(tmp); free(tmp); return PPM_ERR_IO; }
    fclose(f);

    if (rc != CURLE_OK) {
        ppm_error("http: %s (%s)", curl_easy_strerror(rc), url);
        unlink(tmp);
        free(tmp);
        return PPM_ERR_NETWORK;
    }
    (void)code;

    if (rename(tmp, dest_path) != 0) {
        unlink(tmp);
        free(tmp);
        return PPM_ERR_IO;
    }
    free(tmp);
    return PPM_OK;
}
