#include "KMCurl.h"

#include "common/KMLogger.h"

KMCurl::KMCurl()
{

}

KMCurl::~KMCurl()
{

}

/**
 * @brief 通过 curl 命令行下载文件
 *
 * @param url 下载的文件链接
 * @param outputFile  存储到本地的文件路径
 * @return true 下载成功
 * @return false 下载失败
 */
bool KMCurl::curlCmdDownload(string url, string outputFile)
{
    string curlCmd = "curl -o " + outputFile + " -L -f " + url;

    int curlRes = system(curlCmd.c_str());
    if (curlRes != 0)
    {
        kmlogger.error("Failed to download %s file, please check the url(%s) and try again!", outputFile.c_str(), url.c_str());
        return false;
    }
    return true;
}

/**
 * 回调函数，用于处理下载的数据
 */
size_t KMCurl::writeCallback(void *contents, size_t size, size_t nmemb, string *buffer)
{
    size_t totalSize = size * nmemb;
    buffer->append(static_cast<char *>(contents), totalSize);
    return totalSize;
}

/**
 * @brief 通过 curl api 下载文件
 *
 * @param url 下载的文件链接
 * @param outputFile 存储到本地的文件名
 * @return true 下载成功
 * @return false 下载失败
 */
bool KMCurl::curlApiDownload(string url, string outputFile)
{
    // 初始化 libcurl
    CURL *curl = curl_easy_init();
    if (!curl)
    {
        kmlogger.error("Failed to initialize libcurl.");
        return false;
    }

    // 打开文件用于保存下载的数据
    FILE *file = fopen(outputFile.c_str(), "wb");
    if (!file)
    {
        kmlogger.error("Failed to open %s file for writing!", outputFile.c_str());
        curl_easy_cleanup(curl);
        return false;
    }

    // 设置 libcurl 参数
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // 设置超时时间为1秒

    // 执行请求
    CURLcode res = curl_easy_perform(curl);

    // 关闭文件
    fclose(file);

    // 校验下载结果
    if (res != CURLE_OK)
    {
        // 检查是否因为超时而中断
        if (res == CURLE_OPERATION_TIMEDOUT)
        {
            kmlogger.error("Failed to download file, Operation timed out.");
        }
        else
        {
            kmlogger.error("Failed to download file");
        }
        return false;
    }

    // 清理 libcurl
    curl_easy_cleanup(curl);

    return true;
}

/**
 * @brief 通过 curl api 下载二进制文件，比如 .gz 格式压缩文件
 *
 * @param url 下载的文件链接
 * @param outputFileName 输出本地的压缩文件名
 * @return true
 * @return false
 */
bool KMCurl::curlApiDownloadBinaryGZFile(const string &url, const string &outputFileName)
{
    CURL *curl = curl_easy_init();
    if (!curl)
    {
        kmlogger.error("Failed to init curl!");
        return false;
    }

    std::string receivedData;
    std::ofstream outputFile(outputFileName, std::ios::binary); // 以二进制方式打开

    if (!outputFile.is_open())
    {
        kmlogger.error("Failed to open %s file!", outputFileName.c_str());
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &receivedData);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // 设置超时时间为1秒

    CURLcode res = curl_easy_perform(curl);

    if (res != CURLE_OK)
    {
        // 检查是否因为超时而中断
        if (res == CURLE_OPERATION_TIMEDOUT)
        {
            kmlogger.error("Failed to download file, Operation timed out.");
        }
        else
        {
            kmlogger.error("Failed to download file");
        }
        return false;
    }

    outputFile.write(receivedData.c_str(), receivedData.size());

    curl_easy_cleanup(curl);
    outputFile.close();

    return true;
}

bool KMCurl::checkURL(const std::string &url, int timeout)
{
    CURL* curl;
    CURLcode res;
    curl = curl_easy_init();

    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        // 设置超时时间为2秒
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
        // 设置请求不输出
        curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
        // 执行请求
        res = curl_easy_perform(curl);

        // 清理 curl 资源
        curl_easy_cleanup(curl);

        // 判断请求是否成功
        if (res == CURLE_OK)
        {
            return true;
        }
        else
        {
            std::cerr << "Check http url error: " << curl_easy_strerror(res) << std::endl;
        }
    }
    return false;
}
