#include "crypto/tofu_store.h"

#ifdef VOICECAT_HAS_NET

#include <fstream>
#include <sstream>
#include <stdexcept>

namespace voicecat::crypto {

TofuStore::TofuStore(std::filesystem::path path) : path_(std::move(path)) {
    load();
}

TofuResult TofuStore::check_and_pin(const std::string& host, uint16_t port,
                                     const std::array<uint8_t, 32>& fingerprint) {
    std::lock_guard<std::mutex> lk(mu_);
    auto key = make_key(host, port);
    auto it = pins_.find(key);
    if (it == pins_.end()) {
        pins_[key] = fingerprint;
        save();
        return TofuResult::FirstConnect;
    }
    return (it->second == fingerprint) ? TofuResult::Matched : TofuResult::Mismatch;
}

TofuResult TofuStore::peek(const std::string& host, uint16_t port,
                            const std::array<uint8_t, 32>& fingerprint) const {
    std::lock_guard<std::mutex> lk(mu_);
    auto it = pins_.find(make_key(host, port));
    if (it == pins_.end()) return TofuResult::FirstConnect;
    return (it->second == fingerprint) ? TofuResult::Matched : TofuResult::Mismatch;
}

void TofuStore::pin(const std::string& host, uint16_t port,
                     const std::array<uint8_t, 32>& fingerprint) {
    std::lock_guard<std::mutex> lk(mu_);
    pins_[make_key(host, port)] = fingerprint;
    save();
}

void TofuStore::remove(const std::string& host, uint16_t port) {
    std::lock_guard<std::mutex> lk(mu_);
    pins_.erase(make_key(host, port));
    save();
}

std::string TofuStore::make_key(const std::string& host, uint16_t port) {
    return host + ":" + std::to_string(port);
}

std::string TofuStore::fp_to_hex(const std::array<uint8_t, 32>& fp) {
    const char* hex = "0123456789abcdef";
    std::string s;
    s.reserve(64);
    for (auto b : fp) { s += hex[b >> 4]; s += hex[b & 0xf]; }
    return s;
}

std::array<uint8_t, 32> TofuStore::hex_to_fp(const std::string& hex) {
    std::array<uint8_t, 32> fp{};
    if (hex.size() != 64) return fp;
    auto h2n = [](char c) -> uint8_t {
        if (c >= '0' && c <= '9') return c - '0';
        if (c >= 'a' && c <= 'f') return c - 'a' + 10;
        if (c >= 'A' && c <= 'F') return c - 'A' + 10;
        return 0;
    };
    for (size_t i = 0; i < 32; ++i)
        fp[i] = static_cast<uint8_t>((h2n(hex[2*i]) << 4) | h2n(hex[2*i+1]));
    return fp;
}

void TofuStore::load() {
    std::ifstream f(path_);
    if (!f) return;
    std::string line;
    while (std::getline(f, line)) {
        if (line.empty() || line[0] == '#') continue;
        std::istringstream ss(line);
        std::string key, hex;
        if (ss >> key >> hex && hex.size() == 64)
            pins_[key] = hex_to_fp(hex);
    }
}

void TofuStore::save() const {
    std::ofstream f(path_, std::ios::trunc);
    if (!f) throw std::runtime_error("Cannot write TOFU store: " + path_.string());
    for (auto& [key, fp] : pins_)
        f << key << " " << fp_to_hex(fp) << "\n";
}

}  // namespace voicecat::crypto

#endif  // VOICECAT_HAS_NET
