// client.cpp
// 将 DEK 从非可信客户端发送到服务器侧分发 enclave。
// 依赖：OpenSSL、common/verify_callback.cpp、non_enc_client/cert_verify_config.cpp。

#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <memory>
#include <string>
#include <vector>
#include <iostream>

#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/x509_vfy.h>
#include "client_interface.h"
#include "../common/common.h"
#include "../common/verify_callback.h"

namespace kd::client {

namespace {

class OpenSSLGuard {
public:
    OpenSSLGuard() { SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); }
    ~OpenSSLGuard() { ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); }
};

class SocketGuard {
public:
    SocketGuard() = default;
    explicit SocketGuard(int fd) : fd_(fd) {}
    ~SocketGuard() { reset(); }
    int get() const { return fd_; }
    int release() { int tmp = fd_; fd_ = -1; return tmp; }
    void reset(int fd = -1) { if (fd_ >= 0) ::close(fd_); fd_ = fd; }
private:
    int fd_{-1};
};

bool ConnectTcp(const std::string& host, const std::string& port, SocketGuard* out_fd) {
    addrinfo hints{};
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    addrinfo* res = nullptr;
    if (getaddrinfo(host.c_str(), port.c_str(), &hints, &res) != 0)
        return false;

    for (addrinfo* ai = res; ai; ai = ai->ai_next) {
        SocketGuard fd(::socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol));
        if (fd.get() < 0)
            continue;

        if (::connect(fd.get(), ai->ai_addr, ai->ai_addrlen) == 0) {
            out_fd->reset(fd.release());   // 直接把 fd 填进去
            freeaddrinfo(res);
            return true;
        }
    }
    freeaddrinfo(res);
    return false;
}

bool SendAll(SSL* ssl, const uint8_t* data, size_t size) {
    size_t written = 0;
    while (written < size) {
        int ret = SSL_write(ssl, data + written, static_cast<int>(size - written));
        if (ret <= 0)
            return false;
        written += static_cast<size_t>(ret);
    }
    return true;
}

bool RecvExact(SSL* ssl, uint8_t* data, size_t size) {
    size_t read_total = 0;
    while (read_total < size) {
        int ret = SSL_read(ssl, data + read_total, static_cast<int>(size - read_total));
        if (ret <= 0)
            return false;
        read_total += static_cast<size_t>(ret);
    }
    return true;
}

} // namespace

bool SendDekToServer(const ClientConfig& cfg,
                     ClientResult* out_result,
                     std::string* err_msg)
{
    if (cfg.server_host.empty() || cfg.server_port.empty() ||
        cfg.dek.empty() || !out_result)
    {
        std::cerr << "[DEK TLS] invalid client config" << std::endl;
        if (err_msg) *err_msg = "invalid client config";
        return false;
    }

    auto dump_ssl_errors = [&](SSL* ssl, const char* tag, int last_ret = -1) {
    if (!ssl)
    {
        if (err_msg) *err_msg = std::string(tag) + ": no ssl context";
        std::cerr << "[DEK TLS] " << tag << " -> no ssl context" << std::endl;
        return;
    }

    int why = SSL_get_error(ssl, last_ret);
    std::cerr << "[DEK TLS] " << tag << " -> SSL_get_error = "
                << why << std::endl;

    // 打印 errno 及 strerror，便于定位系统调用错误
    int saved_errno = errno;
    std::cerr << "[DEK TLS] " << tag << " -> errno=" << saved_errno
                << " (" << strerror(saved_errno) << ")" << std::endl;
    perror("[DEK TLS] perror");  // 直接输出标准错误描述

    // 打印 OpenSSL 错误栈
    ERR_print_errors_fp(stderr);

    std::string errors;
    unsigned long code = ERR_get_error();
    while (code != 0)
    {
        char buf[256] = {0};
        ERR_error_string_n(code, buf, sizeof(buf));
        if (!errors.empty())
            errors.append("; ");
        errors.append(buf);
        std::cerr << "[DEK TLS]    " << buf << std::endl;
        code = ERR_get_error();
    }

    if (err_msg)
        *err_msg = errors.empty()
                        ? std::string(tag) + ": no error queued"
                        : errors;
    };

    OpenSSLGuard guard;
    std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx(
        SSL_CTX_new(TLS_client_method()), &SSL_CTX_free);
    if (!ctx)
    {
        if (err_msg) *err_msg = "SSL_CTX_new failed";
        std::cerr << "[DEK TLS] SSL_CTX_new failed" << std::endl;
        return false;
    }

    SSL_CTX_set_options(ctx.get(),
                        SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
                        SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
    
    SSL_CTX_set_min_proto_version(ctx.get(), TLS1_2_VERSION);
    SSL_CTX_set_max_proto_version(ctx.get(), TLS1_2_VERSION);

    SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, &verify_callback); 
    // SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, nullptr); 

    SocketGuard sock;
    if (!ConnectTcp(cfg.server_host, cfg.server_port, &sock))
    {
        if (err_msg) *err_msg = "tcp connect failed";
        std::cerr << "[DEK TLS] tcp connect failed" << std::endl;
        return false;
    }

    std::unique_ptr<SSL, decltype(&SSL_free)> ssl(SSL_new(ctx.get()), &SSL_free);
    if (!ssl)
    {
        if (err_msg) *err_msg = "SSL_new failed";
        std::cerr << "[DEK TLS] SSL_new failed" << std::endl;
        return false;
    }

    if (SSL_set_fd(ssl.get(), sock.get()) != 1)
    {
        dump_ssl_errors(ssl.get(), "SSL_set_fd failed", 0);
        return false;
    }

    std::cerr << "[DEK TLS] SSL verify mode: " << SSL_get_verify_mode(ssl.get()) << std::endl;
    std::cerr << "[DEK TLS] SSL verify callback: " << SSL_get_verify_callback(ssl.get()) << std::endl;

    int ret = SSL_connect(ssl.get());
    if (ret != 1)
    {
        dump_ssl_errors(ssl.get(), "SSL_connect failed", ret);
        return false;
    }

    uint32_t len_be = htonl(static_cast<uint32_t>(cfg.dek.size()));
    if (!SendAll(ssl.get(),
                 reinterpret_cast<uint8_t*>(&len_be),
                 sizeof(len_be)))
    {
        dump_ssl_errors(ssl.get(), "SendAll length failed", 0);
        return false;
    }

    if (!SendAll(ssl.get(),
                 cfg.dek.data(),
                 cfg.dek.size()))
    {
        dump_ssl_errors(ssl.get(), "SendAll data failed", 0);
        return false;
    }

    uint32_t hash_be = 0;
    if (!RecvExact(ssl.get(),
                   reinterpret_cast<uint8_t*>(&hash_be),
                   sizeof(hash_be)))
    {
        dump_ssl_errors(ssl.get(), "RecvExact failed", 0);
        return false;
    }

    out_result->dek_digest.assign(
        reinterpret_cast<uint8_t*>(&hash_be),
        reinterpret_cast<uint8_t*>(&hash_be) + sizeof(hash_be));
    std::cerr << "[DEK TLS] handshake and transfer successful" << std::endl;
    return true;
}



} // namespace kd::client
