#include "oss.h"
#include <httplib.h>
#include <map>
#include "common/logging.h"
#include "common/sys_util.h"

namespace zsy
{
    OSS::OSS(const nlohmann::json &config)
        : config(config.get<OSSConfig>().oss)
    {
    }

    std::tuple<bool, std::string> OSS::upload(const std::string &objectName, std::shared_ptr<std::istream> file)
    {
        LOGGER_INFO("正在上传文件：{}", objectName);
        if (!file)
        {
            LOGGER_ERROR("文件为空");
            return std::make_tuple(false, "");
        }
        try
        {
            auto now = std::chrono::system_clock::now();
            auto now_c = std::chrono::system_clock::to_time_t(now);
            tm tm_gmt;
            gmtime_s(&tm_gmt, &now_c);
            std::ostringstream temp;
            temp << std::put_time(&tm_gmt, "%a, %d %b %Y %H:%M:%S GMT");
            std::string date = temp.str();

            std::string str =
                    "PUT\n\napplication/octet-stream\n" +
                    date + "\n" +
                    "/" + config.bucketName + "/" + objectName;

            // 计算签名
            std::string signature = SysUtil::hmac_sha1(config.sk, str);

            // 构建请求
            httplib::Client cli(config.bucketName + "." + config.endpoint);
            cli.set_connection_timeout(5);
            cli.set_read_timeout(10);
            cli.set_write_timeout(10);
            // 设置请求头
            httplib::Headers headers = {
                {"Date", date},
                {"Content-Type", "application/octet-stream"},
                {"Authorization", "OSS " + config.ak + ":" + signature}
            };

            std::stringstream buffer;
            buffer << file->rdbuf();
            const std::string &content = buffer.str();

            // 发送请求
            auto res = cli.Put(("/" + objectName).c_str(), headers, content, "application/octet-stream");

            if (res && res->status == 200)
            {
                std::string body = res && !res->body.empty() ? res->body : "";
                auto url = "https://" + config.bucketName + "." + config.endpoint + "/" + objectName;
                LOGGER_INFO("文件上传成功：{} {} {}", objectName, body, url);
                return std::make_tuple(true, url);
            } else
            {
                std::string body = res && !res->body.empty() ? res->body : "";
                LOGGER_INFO("文件上传失败：{} {}", objectName, body);
                return std::make_tuple(false, "");
            }
        } catch (const std::exception &e)
        {
            LOGGER_INFO("文件上传失败：{} {}", objectName, e.what());
            return std::make_tuple(false, "");
        }catch (...)
        {
            LOGGER_INFO("未知异常，文件上传失败:{}", objectName);
            return std::make_tuple(false, "");
        }
    }

    std::tuple<bool, std::string> OSS::upload(const std::string &objectName, const std::string &filePath)
    {
        if (filePath.empty())
        {
            LOGGER_INFO("文件上传失败，文件路径为空");
            return std::make_tuple(false, "");
        }
        std::shared_ptr<std::ifstream> file = std::make_shared<std::ifstream>(filePath, std::ios::binary);
        if (!file->is_open())
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_INFO("文件上传失败，无法打开文件：{}，错误码：{}，错误信息：{}", filePath, code, msg);
            return std::make_tuple(false, "");
        }
        return upload(objectName, file);
    }
} // zsy
