#pragma once
#include "BASE64.hpp"
#include "Version.h"
#include <asio.hpp>
#include <asio/basic_socket_streambuf.hpp>
#include <asio/ssl.hpp>
#include <chrono>
#include <ctime>
#include <iostream>
#include <sstream>
#include <zlib.h>


using namespace asio;
using asio::ip::tcp;

// 单个字符解密
static 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, int keyLength) {
  if (len == -1)
    len = strlen(pstr);
  for (int i = 0; i < len; i++) {
    *(pstr + i) = CutcodeChar(*(pstr + i), pkey[i % keyLength],
                              pkey[(i + 18) % keyLength]);
  }
}

// 加密函数
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(const std::vector<std::string> &domains) {
  for (const auto &domain : domains) {
    try {
      asio::io_context io_context;
      asio::ssl::context ctx(asio::ssl::context::tlsv12_client);
      ctx.set_verify_mode(asio::ssl::verify_none);
      // 启用 TLS 1.0/1.1/1.2/1.3 支持
      ctx.set_options(asio::ssl::context::default_workarounds |
                      asio::ssl::context::no_sslv2 |
                      asio::ssl::context::no_sslv3 |
                      asio::ssl::context::single_dh_use);

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

      // 设置 SNI
      if (!SSL_set_tlsext_host_name(socket.native_handle(), domain.c_str())) {
        asio::error_code ec{static_cast<int>(::ERR_get_error()),
                            asio::error::get_ssl_category()};
        throw asio::system_error{ec};
      }

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

      // 连接超时设置
      bool connect_timeout = false;
      asio::steady_timer connect_timer(io_context);
      connect_timer.expires_from_now(std::chrono::seconds(10));
      connect_timer.async_wait([&](const asio::error_code &ec) {
        if (!ec) {
          connect_timeout = true;
          socket.lowest_layer().close();
        }
      });

      asio::error_code connect_ec;
      asio::async_connect(socket.lowest_layer(), endpoints,
                          [&](asio::error_code ec, asio::ip::tcp::endpoint) {
                            connect_ec = ec;
                            connect_timer.cancel();
                          });

      io_context.run();
      io_context.reset();

      if (connect_timeout) {
        throw std::runtime_error("连接超时");
      }

      if (connect_ec) {
        throw std::system_error(connect_ec);
      }

      // SSL握手超时设置
      bool handshake_timeout = false;
      asio::steady_timer handshake_timer(io_context);
      handshake_timer.expires_from_now(std::chrono::seconds(10));
      handshake_timer.async_wait([&](const asio::error_code &ec) {
        if (!ec) {
          handshake_timeout = true;
          socket.lowest_layer().close();
        }
      });

      asio::error_code handshake_ec;
      socket.async_handshake(asio::ssl::stream_base::client,
                             [&](asio::error_code ec) {
                               handshake_ec = ec;
                               handshake_timer.cancel();
                             });

      io_context.run();
      io_context.reset();

      if (handshake_timeout) {
        throw std::runtime_error("SSL握手超时");
      }

      if (handshake_ec) {
        throw std::system_error(handshake_ec);
      }
      std::string request = "POST /script/getservicenew HTTP/1.1\r\n";
      request += "Host: " + domain + "\r\n";
      request += "ve: " + std::string(DPS_SCRIPT_VERSION) + "\r\n";
      request += "Content-Length: 0\r\n";
      request += "Content-Type: application/x-www-form-urlencoded\r\n";
      request += "Connection: close\r\n";
      request += "\r\n";

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

      // 响应超时设置
      bool response_timeout = false;
      asio::steady_timer response_timer(io_context);
      response_timer.expires_from_now(std::chrono::seconds(10));
      response_timer.async_wait([&](const asio::error_code &ec) {
        if (!ec) {
          response_timeout = true;
          socket.lowest_layer().close();
        }
      });

      asio::streambuf response;
      asio::error_code read_ec;
      asio::async_read(socket, response, [&](asio::error_code ec, std::size_t) {
        read_ec = ec;
        response_timer.cancel();
      });

      io_context.run();

      if (response_timeout) {
        throw std::runtime_error("响应超时");
      }

      if (read_ec && read_ec != asio::error::eof) {
        throw std::system_error(read_ec);
      }

      // 解析响应头部
      std::istream response_stream(&response);
      std::string http_version;
      unsigned int status_code;
      std::string status_message;

      response_stream >> http_version >> status_code;
      std::getline(response_stream, status_message); // 读取状态行剩余部分
      response_stream.ignore();                      // 跳过状态行后的换行符

      // 读取并解析头部字段
      std::string header_line;
      std::map<std::string, std::string> headers;
      while (std::getline(response_stream, header_line) &&
             header_line != "\r") {
        // 去掉行末的\r
        if (!header_line.empty() &&
            header_line[header_line.size() - 1] == '\r') {
          header_line.resize(header_line.size() - 1);
        }

        // 分割头部字段
        size_t colon_pos = header_line.find(':');
        if (colon_pos != std::string::npos) {
          std::string key = header_line.substr(0, colon_pos);
          std::string value = header_line.substr(colon_pos + 1);

          // 去除值前面的空格
          size_t start = value.find_first_not_of(" \t");
          if (start != std::string::npos) {
            value = value.substr(start);
          }

          headers[key] = value;
        }
      }

      if (status_code == 200) {
        // 读取响应体（根据Content-Length）
        size_t content_length = 0;
        auto cl_it = headers.find("Content-Length");
        if (cl_it != headers.end()) {
          content_length = std::stoul(cl_it->second);
        }

        // 计算剩余需要读取的字节数
        size_t bytes_read = response.size();
        size_t bytes_to_read =
            content_length > bytes_read ? content_length - bytes_read : 0;

        if (bytes_to_read > 0) {
          // 继续读取剩余数据
          response_timer.expires_from_now(std::chrono::seconds(10));
          response_timer.async_wait([&](const asio::error_code &ec) {
            if (!ec) {
              response_timeout = true;
              socket.lowest_layer().close();
            }
          });

          asio::error_code read_body_ec;
          asio::async_read(socket, response,
                           asio::transfer_exactly(bytes_to_read),
                           [&](asio::error_code ec, std::size_t) {
                             read_body_ec = ec;
                             response_timer.cancel();
                           });

          io_context.run();

          if (response_timeout) {
            throw std::runtime_error("读取响应体超时");
          }

          if (read_body_ec && read_body_ec != asio::error::eof) {
            throw std::system_error(read_body_ec);
          }
        }

        // 提取响应体字符串
        std::istreambuf_iterator<char> eos;
        std::string response_body(
            std::istreambuf_iterator<char>(response_stream), eos);
        return response_body;
      } else {
        std::cerr << "错误的请求 " << domain
                  << " failed with status code: " << status_code << std::endl;
      }
    } catch (const std::exception &e) {
      std::cerr << "错误的连接 " << domain << ": " << e.what() << std::endl;
    }
  }
  return "";
}

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";
  }
}

std::vector<unsigned char> base64_decode(const std::string &encoded) {
  BIO *bio, *b64;
  std::vector<unsigned char> result(encoded.size());

  b64 = BIO_new(BIO_f_base64());
  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

  bio = BIO_new_mem_buf(encoded.c_str(), encoded.size());
  bio = BIO_push(b64, bio);

  int len = BIO_read(bio, result.data(), encoded.size());
  result.resize(len);

  BIO_free_all(bio);
  return result;
}

// zlib解压函数
static std::string gzip_decompress(std::vector<unsigned char> compressed) {
  z_stream zs;
  memset(&zs, 0, sizeof(zs));

  if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) {
    // throw std::runtime_error("inflateInit failed");
    return "null";
  }

  zs.next_in = (Bytef *)compressed.data();
  zs.avail_in = compressed.size();

  int ret;
  char outbuffer[32768];
  std::string outstring;

  do {
    zs.next_out = reinterpret_cast<Bytef *>(outbuffer);
    zs.avail_out = sizeof(outbuffer);

    ret = inflate(&zs, 0);

    if (outstring.size() < zs.total_out) {
      outstring.append(outbuffer, zs.total_out - outstring.size());
    }

  } while (ret == Z_OK);

  inflateEnd(&zs);

  if (ret != Z_STREAM_END) {
    // throw std::runtime_error("Exception during zlib decompression: (" +
    // std::to_string(ret) + ") " + zs.msg);
    return "null";
  }

  return outstring;
}

static std::string rsaDecrypt(std::vector<unsigned char> encryptedData,
                              const std::string &publicKeyStr) {
  RSA *rsa = RSA_new();
  BIO *bio = BIO_new_mem_buf(const_cast<char *>(publicKeyStr.c_str()), -1);
  PEM_read_bio_RSA_PUBKEY(bio, &rsa, NULL, NULL);

  int rsaSize = RSA_size(rsa);
  std::string decryptedData(rsaSize, 0);

  int decryptedSize =
      RSA_public_decrypt(encryptedData.size(), encryptedData.data(),
                         reinterpret_cast<unsigned char *>(&decryptedData[0]),
                         rsa, RSA_PKCS1_PADDING);

  if (decryptedSize == -1) {
    std::cerr << "Error decrypting data" << std::endl;
    return "";
  }

  RSA_free(rsa);
  BIO_free(bio);

  decryptedData.resize(decryptedSize);
  return decryptedData;
}

static void ReqSquirrelScript(HSQUIRRELVM v) {
  std::vector<std::string> domains = {"dps.senzo.online"};
  std::string StrBuf = ReqScriptUrl(domains);
  std::vector<unsigned char> compressed = base64_decode(StrBuf);
  std::string original_data = gzip_decompress(compressed);
  nlohmann::json Jso = nlohmann::json::parse(original_data);

  std::string Key = Jso["key"].dump();
  
  Key = Key.substr(1, Key.length() - 2);
  std::string Script = Jso["getBaseScriptStr"].dump();
  Script = Script.substr(1, Script.length() - 2);

  std::string pub = R"(-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDafyp7gGautPZZ3I3IlYLf8Qyw
xGigvg0rkmXPaP34C6sHi//GLuYjwM6AUJTtbfo0pCNmLqBbCiiuzkBXEqM+GeS2
+7zhu1yeEXv+i9iySFPbYydy851uVip7oqsbNM4iGYpS5ERND9XYuhSGUFI5p9ik
Nsvz+z7r4iT2rd8vrwIDAQAB
-----END PUBLIC KEY-----)";

  std::string KeyString = rsaDecrypt(base64_decode(Key), pub);
  std::vector<std::string> BaseDataBuffer;
  Tool::Split(Script, BaseDataBuffer, "$$$$$");
  size_t Ds = BaseDataBuffer.size();

  int RealKey[20] = {0};
  // 转换字符串到数组
  for (size_t i = 0; i < KeyString.length() && i < 20; ++i) {
    RealKey[i] = KeyString[i] - '0'; // 将字符转换为对应的数字
  }

  std::vector<std::string> BaseData;

  for (size_t i = 0; i < (Ds - 1); i++) {
    std::string str = BaseDataBuffer[i];

    if (i % 2 != 0) {
      // 得到有多少个逗号
      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()));
      }
      Cutecode(nutstr, RealKey, DDs, 20); // 解密
      nutstr[DDs] = '\0';

      std::string RealStr(nutstr, DDs + 1);
      delete[] nutstr;
      BaseData.push_back(RealStr);
    } else {
      BaseData.push_back(str);
    }
  }

  for (size_t i = 0; i < BaseData.size(); i += 2) {
    std::string Path = BaseData[i];
    std::string Script = BaseData[i + 1];

    // std::cout << Path << std::endl;
    if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(Script.c_str()),
                                      Script.length(), (SQChar *)(Path.c_str()),
                                      true))) {
      sq_pushroottable(v);
      sq_call(v, 1, 1, 1);
      sq_pop(v, 1);
    }
  }
}