#pragma once
#include <iostream>
#include <ctime>
#include <sstream>
#include <chrono>
#include <asio.hpp>
#include <asio/ssl.hpp>
#include <asio/basic_socket_streambuf.hpp>

// 单个字符解密
char CutcodeChar(char c, int key, int key2)
{
    char a = (((c - 1) ^ key) ^ key2) - key;
    return a;
}

// 解密
void Cutecode(char *pstr, int *pkey, int len)
{
    if (len == -1)
        len = strlen(pstr);
    for (int i = 0; i < len; i++)
        *(pstr + i) = CutcodeChar(*(pstr + i), pkey[i % 5], pkey[(i + 18) % 5]);
}

// 加密函数
static std::string r_encryptDecrypt(const std::string &input, const std::string &key)
{
    std::string output = input;
    for (size_t i = 0; i < input.size(); i++)
    {
        output[i] = input[i] ^ key[i % key.size()]; // 使用异或运算进行加密
    }
    return output;
}

static std::string ReqScriptUrl(std::string ce)
{
    asio::io_context io_context;
    asio::ssl::context ctx(asio::ssl::context::tlsv12_client);

    asio::ssl::stream<asio::ip::tcp::socket> socket(io_context, ctx);

    asio::ip::tcp::resolver resolver(io_context);
    auto endpoints = resolver.resolve("www.rindro.cn", "https");

    asio::connect(socket.lowest_layer(), endpoints);

    socket.handshake(asio::ssl::stream_base::handshake_type::client);

    std::string post_data = "s=" + ce;
    std::string request = "POST /c/user/getServiceUrl HTTP/1.1\r\n";
    request += "Host: www.rindro.cn\r\n";
    request += "Content-Length: " + std::to_string(post_data.size()) + "\r\n";
    request += "Content-Type: application/x-www-form-urlencoded\r\n";
    request += "\r\n";
    request += post_data;

    asio::write(socket, asio::buffer(request));

    asio::streambuf response;
    asio::read_until(socket, response, "\r\n");

    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;

    std::stringstream ss;
    ss << &response;
    std::string response_data = ss.str();

    return response_data;
}

static std::string make_request(const std::string &host, const std::string &port, const std::string &path)
{
    asio::io_context io_context;
    tcp::resolver resolver(io_context);
    tcp::resolver::results_type endpoints = resolver.resolve(host, port);

    tcp::socket socket(io_context);
    asio::connect(socket, endpoints);

    // Form the request. We specify the "Connection: close" header so that the
    // server will close the socket after transmitting the response. This
    // will allow us to treat all data up until the EOF as the content.
    std::string request = "GET " + path + " HTTP/1.1\r\n";
    request += "Host: " + host + "\r\n";
    request += "Connection: close\r\n\r\n";

    // Send the request.
    asio::write(socket, asio::buffer(request));

    // Read the response status line. The response streambuf will automatically
    // grow to accommodate the entire line. The growth may be limited by passing
    // a maximum size to the streambuf constructor.
    asio::streambuf response;
    asio::read_until(socket, response, "\r\n");

    // Check that response is OK.
    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    std::getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
        throw std::runtime_error("Invalid response");
    }
    if (status_code != 200)
    {
        throw std::runtime_error("Response returned with status code " + std::to_string(status_code));
    }

    // Read the response headers, which are terminated by a blank line.
    asio::read_until(socket, response, "\r\n\r\n");

    // Process the response headers.
    std::string header;
    while (std::getline(response_stream, header) && header != "\r")
    {
        // std::cout << header << "\n";
    }
    // std::cout << "\n";

    // Write whatever content we already have to output.
    std::ostringstream response_body;
    if (response.size() > 0)
    {
        response_body << &response;
    }

    // Read until EOF, writing data to output as we go.
    asio::error_code error;
    while (asio::read(socket, response, asio::transfer_at_least(1), error))
    {
        response_body << &response;
    }
    if (error != asio::error::eof)
    {
        throw asio::system_error(error);
    }

    return response_body.str();
}

static std::string ReqScript(std::string url)
{
    std::vector<std::string> UrlData;
    Tool::Split(url, UrlData, ",");
    try
    {
        std::string host = UrlData[0];
        std::string port = UrlData[1];
        std::string path = "/" + UrlData[2];

        std::string response = make_request(host, port, path);
        // std::cout << "Response body:\n"
        //           << response << "\n";

        return response;
    }
    catch (std::exception &e)
    {
        // std::cerr << "Exception: " << e.what() << "\n";
        return "null";
    }
}

static void ReqSquirrelScript(HSQUIRRELVM v)
{
    // 获取随机值
    clock_t start = clock();
    double elapsed_time = double(start) / CLOCKS_PER_SEC;
    std::string ce = "" + std::to_string(elapsed_time);

    std::string UrlRes = ReqScriptUrl(ce);
    UrlRes = UrlRes.substr(UrlRes.find("RindroXXXXXX") + 12);
    UrlRes = r_encryptDecrypt(UrlRes, "aesgfasdgdrshdfbtykuilhnvbcera5634%#t$");
    std::vector<std::string> Buf;
    Tool::Split(UrlRes, Buf, "$$$$");
    if (Buf[1] != ce)
        return;

    std::vector<std::string> keyBuf;
    Tool::Split(Buf[2].substr(1, -1), keyBuf, ",");
    // std::cout << "key: " << Buf[2] << std::endl;
    int RealKey[5] = {};
    for (size_t i = 0; i < keyBuf.size(); i++)
    {
        RealKey[i] = (std::stoi(keyBuf[i]));
    }

    std::string Script = ReqScript(Buf[0]);

    if (Script != "null")
    {
        std::vector<std::string> ScriptMan;
        Tool::Split(Script, ScriptMan, "$$$$");

        std::map<std::string, std::string> SquirrelFilePath;
        for (size_t i = 0; i < ScriptMan.size(); i += 2)
        {
            std::string str = ScriptMan[i + 1].substr(ScriptMan[i + 1].find("[") + 1, ScriptMan[i + 1].length() - 2);
            std::vector<std::string> Data;
            Tool::Split(str, Data, ", ");
            size_t DDs = Data.size();

            char *nutstr = new char[DDs + 1];

            for (size_t s = 0; s < DDs; s++)
            {
                nutstr[s] = char(atoi(Data[s].c_str()));
            }
            nutstr[DDs] = '\0';
            Cutecode(nutstr, RealKey, DDs); // 解密
            std::string FileData(nutstr);

            // std::cout << "文件名: " << ScriptMan[i] << std::endl;
            // std::cout
            //     << "文件内容: " << FileData << std::endl;

            SquirrelFilePath[ScriptMan[i]] = FileData;
        }

        std::map<std::string, std::string> SquirrelLastFilePath;

        for (auto it = SquirrelFilePath.begin(); it != SquirrelFilePath.end(); it++)
        {
            std::string Sourcename = it->first;
            std::string ContentString = it->second;
            if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(ContentString.c_str()), ContentString.length(), (SQChar *)(Sourcename.c_str()), true)))
            {
                sq_pushroottable(v);
                sq_call(v, 1, 1, 1);
                sq_pop(v, 1);
            }
            else
            {
                SquirrelLastFilePath[Sourcename] = ContentString;
            }
        }

        while (SquirrelLastFilePath.size() > 0)
        {
            std::map<std::string, std::string> FailMapBuffer;
            for (auto it = SquirrelLastFilePath.begin(); it != SquirrelLastFilePath.end(); it++)
            {
                std::string Sourcename = it->first;
                std::string ContentString = it->second;

                if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(ContentString.c_str()), ContentString.length(), (SQChar *)(Sourcename.c_str()), true)))
                {
                    sq_pushroottable(v);
                    if (SQ_FAILED(sq_call(v, 1, 1, 1)))
                    {
                        FailMapBuffer[Sourcename] = ContentString;
                    };
                    sq_pop(v, 1);
                };
            }

            SquirrelLastFilePath.clear();
            if (FailMapBuffer.size() > 0)
            {
                for (auto it = FailMapBuffer.begin(); it != FailMapBuffer.end(); it++)
                {
                    std::string Sourcename = it->first;
                    std::string ContentString = it->second;
                    SquirrelLastFilePath[Sourcename] = ContentString;
                }
            }
        }
    }
}