// engine_bridge.cpp — OriginBridge implementation
// Ported from v1-monolithic/include/bridge.hpp (inline methods) → Iter2 modular
// Uses nlohmann/json instead of jsoncpp (Json::Value, Json::Reader, Json::FastWriter)
// HMAC format: ts.nonce.METHOD.path.body (CRY-02 canonical)

#include "engine_bridge.h"
#include "engine_crypto.h"

#include <fstream>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <cstring>
#include <iostream>

using namespace node669427::subsys;

// ──────────────────────────────────────────────────────────────
// Constructor / Destructor
// ──────────────────────────────────────────────────────────────

OriginBridge::OriginBridge(const std::string& origin_url, const std::string& node_id,
                           node669427::CryptoEngine& crypto)
    : origin_url_(origin_url), node_id_(node_id), crypto_ref_(crypto), curl_(curl_easy_init())
{
    if (!curl_) throw std::runtime_error("OriginBridge: curl init failed");
    load_hmac_key();
    LOG(INFO, "OriginBridge: initialized origin=" << origin_url_ << " node=" << node_id_);
}

OriginBridge::~OriginBridge() {
    if (curl_) curl_easy_cleanup(curl_);
}

// ──────────────────────────────────────────────────────────────
// sign_request — CRY-02 HMAC format: ts.nonce.METHOD.path.body
// ──────────────────────────────────────────────────────────────

OriginBridge::SignedRequest OriginBridge::sign_request(
    const std::string& method, const std::string& path, const std::string& body)
{
    SignedRequest req;
    req.method = method;
    req.path = path;
    req.body = body;
    req.x_node_id = node_id_;
    uint32_t now = static_cast<uint32_t>(now_epoch());
    uint64_t cnt = counter_.fetch_add(1);
    req.x_timestamp = std::to_string(now);
    req.x_counter = std::to_string(cnt);

    // CRY-02: sig_payload = ts.nonce.METHOD.path.body
    std::string sig_payload;
    sig_payload.reserve(req.x_timestamp.size() + req.x_counter.size() + method.size() + path.size() + body.size() + 4);
    sig_payload += req.x_timestamp;
    sig_payload += ".";
    sig_payload += req.x_counter;
    sig_payload += ".";
    sig_payload += method;
    sig_payload += ".";
    sig_payload += path;
    sig_payload += ".";
    sig_payload += body;

    // Use CryptoEngine static HMAC
    std::string key_hex = hex_encode(hmac_key_.data(), 32);
    std::string mac_hex = node669427::CryptoEngine::hmacSha256(key_hex, sig_payload);
    req.x_signature = mac_hex;
    return req;
}

// ──────────────────────────────────────────────────────────────
// verify_response / verify_response_hex
// ──────────────────────────────────────────────────────────────

bool OriginBridge::verify_response(const std::vector<uint8_t>& data, const uint8_t* hmac) const {
    if (!hmac) return false;
    std::string data_str(data.begin(), data.end());
    std::string key_hex = hex_encode(hmac_key_.data(), 32);
    std::string computed = node669427::CryptoEngine::hmacSha256(key_hex, data_str);
    std::string provided = hex_encode(hmac, HMAC_LEN);
    return computed == provided;
}

bool OriginBridge::verify_response_hex(const std::vector<uint8_t>& data, const std::string& hmac_hex) const {
    if (hmac_hex.length() != HMAC_LEN * 2) return false;
    std::vector<uint8_t> hmac = hex_decode(hmac_hex);
    if (hmac.size() != HMAC_LEN) return false;
    return verify_response(data, hmac.data());
}

// ──────────────────────────────────────────────────────────────
// send_event — binary event packet with HMAC trailer
// ──────────────────────────────────────────────────────────────

bool OriginBridge::send_event(const SecurityEventPacket& event, const std::string& payload) {
    std::vector<uint8_t> packet;
    packet.reserve(sizeof(SecurityEventPacket) + payload.size() + HMAC_LEN);
    packet.insert(packet.end(), reinterpret_cast<const uint8_t*>(&event),
                  reinterpret_cast<const uint8_t*>(&event) + sizeof(SecurityEventPacket));
    if (!payload.empty()) {
        if (payload.size() > UINT16_MAX) {
            LOG(WARN, "OriginBridge::send_event: payload exceeds 64K, truncating");
        }
        uint16_t pLen = static_cast<uint16_t>(std::min(payload.size(), static_cast<size_t>(UINT16_MAX)));
        packet[22] = static_cast<uint8_t>(pLen & 0xFF);
        packet[23] = static_cast<uint8_t>((pLen >> 8) & 0xFF);
        packet.insert(packet.end(), payload.begin(), payload.begin() + pLen);
    }
    // Compute HMAC for packet
    std::string pkt_str(packet.begin(), packet.end());
    std::string key_hex = hex_encode(hmac_key_.data(), 32);
    std::string mac_hex = node669427::CryptoEngine::hmacSha256(key_hex, pkt_str);
    std::vector<uint8_t> mac = hex_decode(mac_hex);
    packet.insert(packet.end(), mac.begin(), mac.end());

    auto req = sign_request("POST", "/api/events", std::string(packet.begin(), packet.end()));
    return http_post(origin_url_ + req.path, packet, req);
}

// ──────────────────────────────────────────────────────────────
// send_firewall_rules — nlohmann json
// ──────────────────────────────────────────────────────────────

bool OriginBridge::send_firewall_rules(const std::vector<FirewallRule>& rules) {
    nlohmann::json root = nlohmann::json::array();
    for (const auto& r : rules) {
        nlohmann::json obj;
        obj["chain"] = r.chain;
        obj["action"] = r.action;
        obj["protocol"] = r.protocol;
        obj["src"] = r.src;
        obj["dst"] = r.dst;
        obj["dport"] = r.dport;
        obj["comment"] = r.comment;
        obj["expire_at"] = r.expire_at;
        root.push_back(obj);
    }
    std::string body = root.dump();
    auto req = sign_request("POST", "/api/firewall/rules", body);
    return http_post(origin_url_ + req.path, body, req);
}

// ──────────────────────────────────────────────────────────────
// send_heartbeat — nlohmann json
// ──────────────────────────────────────────────────────────────

bool OriginBridge::send_heartbeat(uint8_t cpu, uint8_t mem, uint8_t disk, uint32_t peers,
                                   ServiceState state, NodeRole role) {
    nlohmann::json root;
    root["node_id"] = node_id_;
    root["timestamp"] = now_epoch();
    root["cpu"] = cpu;
    root["mem"] = mem;
    root["disk"] = disk;
    root["peers"] = peers;
    root["state"] = state_name(state);
    root["role"] = role_name(role);
    root["version"] = E_VERSION;
    std::string body = root.dump();
    auto req = sign_request("POST", "/api/heartbeat", body);
    return http_post(origin_url_ + req.path, body, req);
}

// ──────────────────────────────────────────────────────────────
// receive_command — nlohmann json parse
// ──────────────────────────────────────────────────────────────

OriginCommand OriginBridge::receive_command() {
    OriginCommand cmd;
    auto req = sign_request("GET", "/api/commands?node=" + node_id_, "");
    std::string response;
    long http_code = 0;
    if (!http_get(origin_url_ + req.path, response, req, &http_code)) return cmd;
    if (http_code != 200) {
        if (http_code == 204) return cmd;
        LOG(WARN, "OriginBridge::receive_command: HTTP " << http_code);
        return cmd;
    }
    nlohmann::json root;
    try {
        root = nlohmann::json::parse(response);
    } catch (const nlohmann::json::parse_error&) {
        LOG(WARN, "OriginBridge::receive_command: JSON parse failed");
        return cmd;
    }
    if (!root.is_object() || !root.contains("command")) return cmd;
    std::string cmd_str = root["command"].get<std::string>();
    if (cmd_str == "update_rules") cmd.type = UPDATE_RULES;
    else if (cmd_str == "exec_script") cmd.type = EXEC_SCRIPT;
    else if (cmd_str == "reboot") cmd.type = REBOOT;
    else if (cmd_str == "status_req") cmd.type = STATUS_REQ;
    else if (cmd_str == "lockdown") cmd.type = LOCKDOWN;
    else if (cmd_str == "recover") cmd.type = RECOVER;
    else if (cmd_str == "config_push") cmd.type = CONFIG_PUSH;
    else if (cmd_str == "ping") cmd.type = PING;
    else {
        LOG(WARN, "OriginBridge: unknown command: " << cmd_str);
        return cmd;
    }
    if (root.contains("seq")) cmd.seq = root["seq"].get<uint32_t>();
    if (root.contains("timestamp")) cmd.timestamp = root["timestamp"].get<uint32_t>();
    if (root.contains("node_id")) cmd.node_id = root["node_id"].get<uint32_t>();
    if (root.contains("payload")) cmd.payload = root["payload"].get<std::string>();
    if (root.contains("hmac")) {
        std::string hmac_hex = root["hmac"].get<std::string>();
        std::string verify_payload = cmd_str + std::to_string(cmd.seq) +
            std::to_string(cmd.timestamp) + cmd.payload;
        std::vector<uint8_t> hmac_bytes = hex_decode(hmac_hex);
        std::string key_hex = hex_encode(hmac_key_.data(), 32);
        std::string computed = node669427::CryptoEngine::hmacSha256(key_hex, verify_payload);
        if (computed != hmac_hex) {
            LOG(WARN, "OriginBridge::receive_command: HMAC verify FAILED");
            return OriginCommand{};
        }
    }
    if (!check_replay(cmd.seq, cmd.timestamp)) {
        LOG(WARN, "OriginBridge::receive_command: replay detected seq=" << cmd.seq << " ts=" << cmd.timestamp);
        return OriginCommand{};
    }
    last_seq_ = cmd.seq;
    last_ts_ = cmd.timestamp;
    LOG(INFO, "OriginBridge: received command " << cmd_str << " seq=" << cmd.seq);
    return cmd;
}

// ──────────────────────────────────────────────────────────────
// report_result — nlohmann json
// ──────────────────────────────────────────────────────────────

bool OriginBridge::report_result(uint32_t seq, bool success, const std::string& output, const std::string& error) {
    nlohmann::json root;
    root["node_id"] = node_id_;
    root["seq"] = seq;
    root["success"] = success;
    root["output"] = output;
    if (!error.empty()) root["error"] = error;
    root["timestamp"] = now_epoch();
    std::string body = root.dump();
    auto req = sign_request("POST", "/api/commands/result", body);
    return http_post(origin_url_ + req.path, body, req);
}

// ──────────────────────────────────────────────────────────────
// send_status_report — nlohmann json
// ──────────────────────────────────────────────────────────────

bool OriginBridge::send_status_report(NodeRole role, ServiceState state, const std::string& details) {
    nlohmann::json root;
    root["node_id"] = node_id_;
    root["timestamp"] = now_epoch();
    root["role"] = role_name(role);
    root["state"] = state_name(state);
    root["details"] = details;
    root["version"] = E_VERSION;
    std::string body = root.dump();
    auto req = sign_request("POST", "/api/status", body);
    return http_post(origin_url_ + req.path, body, req);
}

// ──────────────────────────────────────────────────────────────
// Private helpers
// ──────────────────────────────────────────────────────────────

void OriginBridge::load_hmac_key() {
    std::string key_path = std::string(E_KEYS) + "/" + node_id_ + ".hmac";
    std::ifstream fs(key_path);
    if (fs.is_open()) {
        std::string hex;
        fs >> hex;
        fs.close();
        if (hex.length() == 64) {
            std::vector<uint8_t> raw = hex_decode(hex);
            if (raw.size() == HMAC_LEN) {
                std::copy(raw.begin(), raw.end(), hmac_key_.data());
                LOG(INFO, "OriginBridge: loaded HMAC key from " << key_path);
                return;
            }
        }
    }
    // Fallback: derive from CryptoEngine
    std::string derived = node669427::CryptoEngine::hmacSha256(
        "HMAC-DERIVE-2024", node_id_);
    std::vector<uint8_t> seed = hex_decode(derived);
    std::copy(seed.begin(), seed.begin() + std::min(seed.size(), size_t(32)), hmac_key_.data());
    LOG(WARN, "OriginBridge: using derived HMAC key");
}

bool OriginBridge::check_replay(uint32_t seq, uint32_t timestamp) {
    uint32_t now = static_cast<uint32_t>(now_epoch());
    if (timestamp > now + TS_WINDOW_S || timestamp + TS_WINDOW_S < now) return false;
    uint32_t prev = last_seq_.load();
    if (seq <= prev && (prev - seq) > 10) return false;
    uint32_t prev_ts = last_ts_.load();
    if (timestamp < prev_ts && (prev_ts - timestamp) > TS_WINDOW_S) return false;
    return true;
}

size_t OriginBridge::curl_write_cb(void* contents, size_t size, size_t nmemb, void* userp) {
    size_t total = size * nmemb;
    static_cast<std::string*>(userp)->append(static_cast<char*>(contents), total);
    return total;
}

struct curl_slist* OriginBridge::build_headers(const SignedRequest& req, const std::string& content_type) const {
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, ("X-Node-ID: " + req.x_node_id).c_str());
    headers = curl_slist_append(headers, ("X-Timestamp: " + req.x_timestamp).c_str());
    headers = curl_slist_append(headers, ("X-Counter: " + req.x_counter).c_str());
    headers = curl_slist_append(headers, ("X-Signature: " + req.x_signature).c_str());
    headers = curl_slist_append(headers, ("Content-Type: " + content_type).c_str());
    return headers;
}

bool OriginBridge::http_post(const std::string& url, const std::vector<uint8_t>& data, const SignedRequest& req) {
    return http_post(url, std::string(data.begin(), data.end()), req);
}

bool OriginBridge::http_post(const std::string& url, const std::string& body, const SignedRequest& req) {
    if (!curl_) return false;
    CURL* local = curl_easy_init();
    if (!local) return false;
    std::string response;
    struct curl_slist* headers = build_headers(req, "application/json");
    curl_easy_setopt(local, CURLOPT_URL, url.c_str());
    curl_easy_setopt(local, CURLOPT_POSTFIELDS, body.c_str());
    curl_easy_setopt(local, CURLOPT_POSTFIELDSIZE, (long)body.size());
    curl_easy_setopt(local, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(local, CURLOPT_WRITEFUNCTION, curl_write_cb);
    curl_easy_setopt(local, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(local, CURLOPT_TIMEOUT, 30L);
    curl_easy_setopt(local, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(local, CURLOPT_NOSIGNAL, 1L);
    CURLcode res = curl_easy_perform(local);
    long http_code = 0;
    curl_easy_getinfo(local, CURLINFO_RESPONSE_CODE, &http_code);
    curl_easy_cleanup(local);
    curl_slist_free_all(headers);
    if (res != CURLE_OK) {
        LOG(WARN, "OriginBridge::http_post: curl error: " << curl_easy_strerror(res));
        return false;
    }
    if (http_code < 200 || http_code >= 300) {
        LOG(WARN, "OriginBridge::http_post: HTTP " << http_code << " from " << url);
        return false;
    }
    return true;
}

bool OriginBridge::http_get(const std::string& url, std::string& response, const SignedRequest& req, long* out_http_code) {
    if (!curl_) return false;
    CURL* local = curl_easy_init();
    if (!local) return false;
    struct curl_slist* headers = build_headers(req, "application/json");
    curl_easy_setopt(local, CURLOPT_URL, url.c_str());
    curl_easy_setopt(local, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(local, CURLOPT_WRITEFUNCTION, curl_write_cb);
    curl_easy_setopt(local, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(local, CURLOPT_TIMEOUT, 30L);
    curl_easy_setopt(local, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(local, CURLOPT_NOSIGNAL, 1L);
    CURLcode res = curl_easy_perform(local);
    long http_code = 0;
    curl_easy_getinfo(local, CURLINFO_RESPONSE_CODE, &http_code);
    curl_easy_cleanup(local);
    curl_slist_free_all(headers);
    if (res != CURLE_OK) {
        LOG(WARN, "OriginBridge::http_get: curl error: " << curl_easy_strerror(res));
        return false;
    }
    if (out_http_code) *out_http_code = http_code;
    if (http_code != 200 && http_code != 204) {
        LOG(WARN, "OriginBridge::http_get: HTTP " << http_code << " from " << url);
        return false;
    }
    return true;
}
