/**
  ******************************************************************************
  * @file           : Api.cpp
  * @author         : Dream
  * @brief          : Implementation of API class
  * @attention      : None
  * @date           : 24-10-3
  ******************************************************************************
  */

#include "Api.hpp"
#include <iomanip>
#include <sstream>
#include "Utils.hpp"
#include "Encryption.hpp"
#include "Network.hpp"
#include "decrypt.hpp"
#include "log.hpp"

API::API(std::string kami, std::string device_key)
    : kami(std::move(kami)), device_key(std::move(device_key)) {}

std::string API::generate_sign(const time_t tt,const std::string_view extra) const {
    std::stringstream sign_data;
    sign_data << std::string(AY_OBFUSCATE("kami=")) << kami
              << std::string(AY_OBFUSCATE("&markcode=")) << device_key
              << std::string(AY_OBFUSCATE("&t=")) << tt
              << std::string(AY_OBFUSCATE("&")) << appkey << extra;
    return Utils::generate_md5(sign_data.str());
}

nlohmann::json API::send_encrypted_request(const std::string_view data_to_encrypt, const std::string& key) {
    const std::string public_key = decrypt_string_B(RSA_PRIVATE_A, RSA_PRIVATE_B, RSA_PRIVATE_Key_A, RSA_PRIVATE_Key_B);
    const std::string encrypted_data = Encryption::enRas(std::string(data_to_encrypt), public_key);
    const std::string response = Network::post_request(decrypt_string_B(base_url_A, base_url_B, base_url_Key_A, base_url_Key_B), encrypted_data);
    LOG_DEBUG(std::string(AY_OBFUSCATE("请求响应: %s")).c_str(), response.c_str());
    std::string decrypted_response = Encryption::deRas(response, key);
    LOG_DEBUG(std::string(AY_OBFUSCATE("解密响应: %s")).c_str(), decrypted_response.c_str());
    return nlohmann::json::parse(decrypted_response);
}

bool API::single_code_login(LoginResponse& ret) const {
    time_t tt = std::time(nullptr);
    const std::string timestamp = Utils::generate_value(tt);
    const std::string sign = generate_sign(tt);
    std::stringstream data_to_encrypt;
    data_to_encrypt << std::string(AY_OBFUSCATE("id=")) << login_id
                    << std::string(AY_OBFUSCATE("&kami=")) << kami
                    << std::string(AY_OBFUSCATE("&markcode=")) << device_key
                    << std::string(AY_OBFUSCATE("&t=")) << tt
                    << std::string(AY_OBFUSCATE("&sign=")) << sign
                    << std::string(AY_OBFUSCATE("&value=")) << timestamp;

    ret = parseLoginResponse(send_encrypted_request(data_to_encrypt.str(),decrypt_string_B(LoginAPI1_A, LoginAPI1_B, LoginAPI1_Key_A, LoginAPI1_Key_B)));
    LOG_DEBUG(std::string(AY_OBFUSCATE("check [%d]")).c_str(), ret.code);
    if (ret.code == Local_heck_code) {
        if (Utils::generate_md5(std::to_string(ret.time) + appkey + timestamp) == ret.msg.check) {
            ret.login = true;
            return true;
        }
    }
    ret.login = false;
    return false;
}


nlohmann::json API::get_announcement() {
    std::stringstream data_to_encrypt;
    data_to_encrypt << std::string(AY_OBFUSCATE("id=")) << announcement_id;
    return send_encrypted_request(data_to_encrypt.str(),decrypt_string_B(Notice_A, Notice_B, Notice_Key_A, Notice_Key_B));
}

nlohmann::json API::get_updates() const {
    std::stringstream data_to_encrypt;
    data_to_encrypt << std::string(AY_OBFUSCATE("id=")) << update_id;
    return send_encrypted_request(data_to_encrypt.str(),decrypt_string_B(update_configuration_A, update_configuration_B, update_configuration_Key_A, update_configuration_Key_B));
}

uint64_t API::get_remote_variable(std::string_view variable_name, std::string_view token) const {
    time_t tt = std::time(nullptr);
    std::string timestamp = Utils::generate_value(tt);
    std::stringstream sign;
    sign << std::string(AY_OBFUSCATE("usetoken=")) << token
         << std::string(AY_OBFUSCATE("&kami=")) << kami
         << std::string(AY_OBFUSCATE("&markcode=")) << device_key
         << std::string(AY_OBFUSCATE("&t=")) << tt
         << std::string(AY_OBFUSCATE("&")) << std::string(AY_OBFUSCATE("aTemA5Qm")) << std::string(AY_OBFUSCATE("=")) << variable_name
         << std::string(AY_OBFUSCATE("&")) << appkey;
    std::string sign_str = Utils::generate_md5(sign.str());

    std::stringstream data_to_encrypt;
    data_to_encrypt << std::string(AY_OBFUSCATE("id=")) << remote_variable_id
                    << std::string(AY_OBFUSCATE("&usetoken=")) << token
                    << std::string(AY_OBFUSCATE("&kami=")) << kami
                    << std::string(AY_OBFUSCATE("&markcode=")) << device_key
                    << std::string(AY_OBFUSCATE("&t=")) << tt
                    << std::string(AY_OBFUSCATE("&value=")) << timestamp
                    << std::string(AY_OBFUSCATE("&sign=")) << sign_str
                    << std::string(AY_OBFUSCATE("&")) << std::string(AY_OBFUSCATE("aTemA5Qm")) << std::string(AY_OBFUSCATE("=")) << variable_name;
    if (auto TheServerReturnsInformation = send_encrypted_request(data_to_encrypt.str(),decrypt_string_B(RemoteVariables_A, RemoteVariables_B, RemoteVariables_Key_A, RemoteVariables_Key_B)); TheServerReturnsInformation.contains(std::string(AY_OBFUSCATE("code"))) && TheServerReturnsInformation[std::string(AY_OBFUSCATE("code"))].is_number_integer() && TheServerReturnsInformation[std::string(AY_OBFUSCATE("code"))].get<int>() == remote_variable_code) {
        if (Utils::generate_md5(std::to_string(TheServerReturnsInformation[std::string(AY_OBFUSCATE("time"))].get<time_t>()) + appkey + timestamp) == TheServerReturnsInformation[std::string(AY_OBFUSCATE("msg"))][std::string(AY_OBFUSCATE("check"))].get<std::string>()) {
            return std::stoull(TheServerReturnsInformation[std::string(AY_OBFUSCATE("msg"))][std::string(AY_OBFUSCATE("msg"))].get<std::string>(), nullptr, 16);
        }
    }
    return 0;
}


LoginResponse API::parseLoginResponse(const nlohmann::json& root) {
    LoginResponse response = {};
    auto json_str = root.dump();
    LOG_DEBUG(std::string(AY_OBFUSCATE("JSON 内容: %s")).c_str(), json_str.c_str());

    if (root.is_null()) {
        return response;
    }

    if (root.contains(std::string(AY_OBFUSCATE("code"))) && root[std::string(AY_OBFUSCATE("code"))].is_number_integer()) {
        response.code = root[std::string(AY_OBFUSCATE("code"))].get<int>();
    }

    if (root.contains(std::string(AY_OBFUSCATE("msg"))) && root[std::string(AY_OBFUSCATE("msg"))].is_object()) {
        const auto& msg = root[std::string(AY_OBFUSCATE("msg"))];
        if (msg.contains(std::string(AY_OBFUSCATE("id"))) && msg[std::string(AY_OBFUSCATE("id"))].is_number_integer()) {
            response.msg.id = msg[std::string(AY_OBFUSCATE("id"))].get<time_t>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("kmtype"))) && msg[std::string(AY_OBFUSCATE("kmtype"))].is_string()) {
            response.msg.kmtype = msg[std::string(AY_OBFUSCATE("kmtype"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("token"))) && msg[std::string(AY_OBFUSCATE("token"))].is_string()) {
            response.msg.token = msg[std::string(AY_OBFUSCATE("token"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("ktype"))) && msg[std::string(AY_OBFUSCATE("ktype"))].is_string()) {
            response.msg.ktype = msg[std::string(AY_OBFUSCATE("ktype"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("vip"))) && msg[std::string(AY_OBFUSCATE("vip"))].is_number_integer()) {
            response.msg.vip = msg[std::string(AY_OBFUSCATE("vip"))].get<time_t>();
            response.msg.expireDateTime = convertTimestampToDateTime(response.msg.vip);
        }
        if (msg.contains(std::string(AY_OBFUSCATE("check"))) && msg[std::string(AY_OBFUSCATE("check"))].is_string()) {
            response.msg.check = msg[std::string(AY_OBFUSCATE("check"))].get<std::string>();
        }
    }

    if (root.contains(std::string(AY_OBFUSCATE("time"))) && root[std::string(AY_OBFUSCATE("time"))].is_number_integer()) {
        response.time = root[std::string(AY_OBFUSCATE("time"))].get<time_t>();
        response.msg.dateTime = convertTimestampToDateTime(response.time);
    }

    return response;
}

void API::parseAnnouncementResponse(AnnouncementResponse& response) {
    nlohmann::json root = get_announcement();
    auto json_str = root.dump();
    LOG_DEBUG(std::string(AY_OBFUSCATE("公告 JSON: %s")).c_str(), json_str.c_str());

    if (root.is_null()) {
        return;
    }

    if (root.contains(std::string(AY_OBFUSCATE("code"))) && root[std::string(AY_OBFUSCATE("code"))].is_number_integer()) {
        response.code = root[std::string(AY_OBFUSCATE("code"))].get<int>();
    }

    if (root.contains(std::string(AY_OBFUSCATE("msg"))) && root[std::string(AY_OBFUSCATE("msg"))].is_object()) {
        const auto& msg = root[std::string(AY_OBFUSCATE("msg"))];
        if (msg.contains(std::string(AY_OBFUSCATE("app_gg"))) && msg[std::string(AY_OBFUSCATE("app_gg"))].is_string()) {
            response.msg.app_gg = msg[std::string(AY_OBFUSCATE("app_gg"))].get<std::string>();
        }
    }

    if (root.contains(std::string(AY_OBFUSCATE("time"))) && root[std::string(AY_OBFUSCATE("time"))].is_number_integer()) {
        response.time = root[std::string(AY_OBFUSCATE("time"))].get<time_t>();
        response.dateTime = convertTimestampToDateTime(response.time);
    }
}

UpdateInfo API::parseUpdateResponse() const {
    UpdateInfo info = {};
    nlohmann::json root = get_updates();
    auto json_str = root.dump();
    LOG_DEBUG(std::string(AY_OBFUSCATE("更新 JSON: %s")).c_str(), json_str.c_str());

    if (root.is_null()) {
        return info;
    }

    if (root.contains(std::string(AY_OBFUSCATE("code"))) && root[std::string(AY_OBFUSCATE("code"))].is_number_integer()) {
        info.code = root[std::string(AY_OBFUSCATE("code"))].get<int>();
    }

    if (root.contains(std::string(AY_OBFUSCATE("msg"))) && root[std::string(AY_OBFUSCATE("msg"))].is_object()) {
        const auto& msg = root[std::string(AY_OBFUSCATE("msg"))];
        if (msg.contains(std::string(AY_OBFUSCATE("msg"))) && msg[std::string(AY_OBFUSCATE("msg"))].is_string()) {
            info.msg = msg[std::string(AY_OBFUSCATE("msg"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("version"))) && msg[std::string(AY_OBFUSCATE("version"))].is_string()) {
            info.version = msg[std::string(AY_OBFUSCATE("version"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("updateshow"))) && msg[std::string(AY_OBFUSCATE("updateshow"))].is_string()) {
            info.updateshow = msg[std::string(AY_OBFUSCATE("updateshow"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("updateurl"))) && msg[std::string(AY_OBFUSCATE("updateurl"))].is_string()) {
            info.updateurl = msg[std::string(AY_OBFUSCATE("updateurl"))].get<std::string>();
        }
        if (msg.contains(std::string(AY_OBFUSCATE("updatemust"))) && msg[std::string(AY_OBFUSCATE("updatemust"))].is_string()) {
            info.updatemust = msg[std::string(AY_OBFUSCATE("updatemust"))].get<std::string>();
        }
    }

    if (root.contains(std::string(AY_OBFUSCATE("time"))) && root[std::string(AY_OBFUSCATE("time"))].is_number_integer()) {
        info.time = root[std::string(AY_OBFUSCATE("time"))].get<time_t>();
    }

    return info;
}

void API::parseCustomResponse(CustomResponse& ret) const {
    time_t tt;
    std::string timestamp = Utils::generate_value(tt);
    std::stringstream sign;
    sign    << std::string(AY_OBFUSCATE("kami=")) << kami
            << std::string(AY_OBFUSCATE("&markcode=")) << device_key
            << std::string(AY_OBFUSCATE("&t=")) << tt
            << std::string(AY_OBFUSCATE("&kamitoken=")) << ret.token
            << std::string(AY_OBFUSCATE("&")) << appkey;
    std::string sign_md5 = Utils::generate_md5(sign.str());
    std::stringstream data_to_encrypt;
    data_to_encrypt << std::string(AY_OBFUSCATE("id=")) << heartbeat_id
                    << std::string(AY_OBFUSCATE("&kami=")) << kami
                    << std::string(AY_OBFUSCATE("&markcode=")) << device_key
                    << std::string(AY_OBFUSCATE("&t=")) << tt
                    << std::string(AY_OBFUSCATE("&sign=")) << sign_md5
                    << std::string(AY_OBFUSCATE("&kamitoken=")) << ret.token
                    << std::string(AY_OBFUSCATE("&value=")) << timestamp;

    if (nlohmann::json root = send_encrypted_request(data_to_encrypt.str(),decrypt_string_B(HeartbeatVerification20_A, HeartbeatVerification20_B, HeartbeatVerification20_Key_A, HeartbeatVerification20_Key_B)); root.contains(std::string(AY_OBFUSCATE("code"))) && root[std::string(AY_OBFUSCATE("code"))].is_number_integer() && root[std::string(AY_OBFUSCATE("code"))].get<int>() == heartbeat_code) {
        if (Utils::generate_md5(std::to_string(root[std::string(AY_OBFUSCATE("time"))].get<time_t>()) + appkey + timestamp) == root[std::string(AY_OBFUSCATE("msg"))][std::string(AY_OBFUSCATE("check"))].get<std::string>()) {
            auto json_str = root.dump();
            LOG_DEBUG(std::string(AY_OBFUSCATE("心跳 JSON: %s")).c_str(), json_str.c_str());
            if (root.contains(std::string(AY_OBFUSCATE("code"))) && root[std::string(AY_OBFUSCATE("code"))].is_number_integer()) {
                ret.code = root[std::string(AY_OBFUSCATE("code"))].get<int>();
            }

            if (root.contains(std::string(AY_OBFUSCATE("msg"))) && root[std::string(AY_OBFUSCATE("msg"))].is_object()) {
                const auto& msg = root[std::string(AY_OBFUSCATE("msg"))];
                if (msg.contains(std::string(AY_OBFUSCATE("endtime"))) && msg[std::string(AY_OBFUSCATE("endtime"))].is_number_integer()) {
                    ret.msg.endtime = msg[std::string(AY_OBFUSCATE("endtime"))].get<time_t>();
                }
                if (msg.contains(std::string(AY_OBFUSCATE("type"))) && msg[std::string(AY_OBFUSCATE("type"))].is_string()) {
                    ret.msg.type = msg[std::string(AY_OBFUSCATE("type"))].get<std::string>();
                }
                if (msg.contains(std::string(AY_OBFUSCATE("timetype"))) && msg[std::string(AY_OBFUSCATE("timetype"))].is_string()) {
                    ret.msg.timetype = msg[std::string(AY_OBFUSCATE("timetype"))].get<std::string>();
                }
                if (msg.contains(std::string(AY_OBFUSCATE("check"))) && msg[std::string(AY_OBFUSCATE("check"))].is_string()) {
                    ret.msg.check = msg[std::string(AY_OBFUSCATE("check"))].get<std::string>();
                }
            }

            if (root.contains(std::string(AY_OBFUSCATE("time"))) && root[std::string(AY_OBFUSCATE("time"))].is_number_integer()) {
                ret.time = root[std::string(AY_OBFUSCATE("time"))].get<time_t>();
                convertSecondsToTime(calculateRemainingTime(ret), ret.timeStr);
            }
        }
    }
    if (ret.code != heartbeat_code) {
        LOG_DEBUG(std::string(AY_OBFUSCATE("心跳异常 %d")).c_str(), 0);
        exit(0);
    }
}

std::string API::convertTimestampToDateTime(const time_t timestamp) {
    const std::tm* timeInfo = std::gmtime(&timestamp);
    std::stringstream oss;
    oss << std::put_time(timeInfo, std::string(AY_OBFUSCATE("%Y-%m-%d %H:%M:%S")).c_str());
    return oss.str();
}

time_t calculateRemainingTime(const CustomResponse& response) {
    return response.msg.endtime - response.time;
}

void convertSecondsToTime(time_t seconds, std::string& timeStr) {
    if (seconds < 0) {
        LOG_DEBUG(std::string(AY_OBFUSCATE("卡密已过期 程序即将退出[%d]")).c_str(),0);
        std::abort();
    }

    const int days = seconds / 86400;
    seconds %= 86400;
    const int hours = seconds / 3600;
    seconds %= 3600;
    const int minutes = seconds / 60;
    seconds %= 60;

    std::ostringstream oss;
    oss << std::string(AY_OBFUSCATE("到期时间: ")) << days << std::string(AY_OBFUSCATE(" 天 "))
        << std::setw(2) << std::setfill('0') << hours << std::string(AY_OBFUSCATE(" 小时 "))
        << std::setw(2) << std::setfill('0') << minutes << std::string(AY_OBFUSCATE(" 分钟 "))
        << std::setw(2) << std::setfill('0') << seconds << std::string(AY_OBFUSCATE(" 秒"));
    timeStr = oss.str();
}