#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>

#include <jsc/client.hpp>
#include <jsc/client_config.hpp>
#include <jsc/client_key.hpp>
#include <jsc/detail/encoding.hpp>
#include <jsc/logger.hpp>

#include <httplib.h>
#include <nlohmann/json.hpp>

using namespace jsc;
using namespace jsc::detail;

static std::string_view bytes_to_str(std::vector<uint8_t> const &bytes) {
  return {reinterpret_cast<char const *>(bytes.data()), bytes.size()};
}

#define ASSERT(cond)                                                           \
  if (!(cond))                                                                 \
    throw std::runtime_error{"Assertion failed"};

static constexpr auto kClientConfigPath = "example/client_config.json";

static constexpr auto kServerAddr = "http://127.0.0.1:8080";

void test_encoding() {
  for (auto [s, v] : std::map<std::string_view, char const *>{
           {"hello, world", "aGVsbG8sIHdvcmxk"},
           {"hello, worl", "aGVsbG8sIHdvcmw="},
           {"hello, wor", "aGVsbG8sIHdvcg=="},
       }) {

    ASSERT(base64_encode(reinterpret_cast<uint8_t const *>(s.data()),
                         s.size()) == v);

    ASSERT(bytes_to_str(base64_decode(v)) == s);
  }
}

static constexpr char kRootKeyBlob[] = {
#include "example/myPrivateKey.blob"
};

static ClientConfig get_client_config() {
  auto client_config = ClientConfig::from_file(kClientConfigPath);

  client_config.root_key_config = {
      {"test_id_test_name", {kRootKeyBlob, sizeof(kRootKeyBlob)}}};

  return client_config;
}

void test_attest_server() {
  auto client_config = get_client_config();
  std::cout << client_config.ra_url << std::endl
            << client_config.bytedance_top_info << std::endl;

  Client client{client_config};
  client.attest_server();
}

void test_encrypt() {
  Client client{get_client_config()};
  client.attest_server();

  auto message = "我是待加密字符串";
  auto encrypted = client.encrypt(message);
  std::cout << "Encrypted: " << encrypted << std::endl;
}

template <typename... Args>
static nlohmann::json request_post(std::string const &path, Args &&...args) {
  httplib::Client cli{kServerAddr};
  std::cout << "Requesting " << path << std::endl;
  auto response = cli.Post(path, std::forward<Args>(args)...);
  if (!response) {
    std::cout << "Network error: " << httplib::to_string(response.error())
              << std::endl;
    return {};
  }
  std::cout << "Response: " << response->body << std::endl;
  if (response->status != 200) {
    std::cout << "Response status: " << response->status << std::endl;
    return {};
  }

  try {
    auto response_json = nlohmann::json::parse(response->body);
    return response_json;
  } catch (nlohmann::json::exception const &) {
    return {};
  }
}

void test_request() {
  Client client{get_client_config()};
  client.attest_server();

  auto message = "我是机密信息";
  auto encrypted = client.encrypt(message);

  // 发送网络请求
  nlohmann::json request{
      {"enc_msg", encrypted},
  };
  (void)request_post("/request_example_1", request.dump(), "application/json");
}

void test_request_with_response() {
  Client client{get_client_config()};
  client.attest_server();

  auto message = "我是机密信息";
  auto [encrypted, response_key] = client.encrypt_with_response(message);

  // 发送网络请求
  nlohmann::json request{
      {"enc_msg", encrypted},
  };
  auto response =
      request_post("/request_example_1", request.dump(), "application/json");
  if (response.is_null())
    return;

  // 对服务端的应答中的加密信息进行解密
  auto response_message =
      base64_decode_string(response["msg"].get<std::string_view>());
  std::cout << "Response: " << response_message << std::endl;

  auto decrypted_response = response_key.decrypt(response_message);
  std::cout << "Decrypted: " << bytes_to_str(decrypted_response) << std::endl;
}

static constexpr wchar_t kPlaintextFile[] = L"test原始文件";
static constexpr wchar_t kEncryptedFile[] = L"test加密文件";

void test_request_with_file() {
  Client client{get_client_config()};
  client.attest_server();

  {
    auto message = "我是机密信息";
    std::ofstream file(kPlaintextFile, std::ios::binary);
    for (int i = 0; i < 1000; ++i)
      file << message << i;
  }
  auto metadata = client.encrypt_file(kPlaintextFile, kEncryptedFile);
  std::string encrypted;
  {
    std::ifstream file{kEncryptedFile, std::ios::binary};
    std::stringstream buffer;
    buffer << file.rdbuf();
    encrypted = buffer.str();
  }
  std::cout << "Metadata: " << metadata << std::endl;

  // 发送网络请求
  httplib::Headers headers{{"X-File-Enc-Key", metadata}};
  (void)request_post("/request_example_3", headers, encrypted,
                     "application/octet-stream");
}

void test_sign() {
  Client client{get_client_config()};
  client.attest_server();

  auto message = "我是机密信息";
  auto encrypted = client.encrypt(message);

  auto app_info = "test_id_test_name";
  nlohmann::json request{
      {"enc_msg", encrypted},
      {"app_info", app_info},
  };

  // 签名
  auto timestamp_str = std::to_string(std::time(nullptr));
  timestamp_str = "1739186828"; // FIXME:

  auto sign = client.gen_sign(app_info, timestamp_str);
  std::cout << "Signature: " << sign << std::endl;

  // 发送网络请求
  httplib::Headers headers{{"Timestamp", timestamp_str}, {"Sign", sign}};
  (void)request_post("/sign_example_1", headers, request.dump(),
                     "application/json");
}

int main() {
  std::cout << "Start example" << std::endl;

  test_encoding();

  test_attest_server();

  test_encrypt();

  test_request();

  test_request_with_file();

  test_request_with_response();

  test_sign();

  return 0;
}
