#pragma once

#include "common.hpp"
#include "crypto.hpp"
#include <curl/curl.h>
#include <json/json.h>
#include <atomic>



enum CmdType : uint8_t {
    UNKNOWN = 0,
    UPDATE_RULES = 1,
    EXEC_SCRIPT = 2,
    REBOOT = 3,
    STATUS_REQ = 4,
    LOCKDOWN = 5,
    RECOVER = 6,
    CONFIG_PUSH = 7,
    PING = 8
};
typedef CmdType OriginCommandType;

struct OriginCommand {
    CmdType type = UNKNOWN;
    uint32_t seq = 0;
    uint32_t timestamp = 0;
    uint32_t node_id = 0;
    std::string payload;
    std::vector<uint8_t> hmac;

    bool valid() const { return type != UNKNOWN; }
};

struct FirewallRule {
    std::string chain;
    std::string action;
    std::string protocol;
    std::string src;
    std::string dst;
    uint16_t dport = 0;
    std::string comment;
    uint32_t expire_at = 0;
};

class OriginBridge {
public:
    struct SignedRequest {
        std::string method;
        std::string path;
        std::string body;
        std::string x_node_id;
        std::string x_timestamp;
        std::string x_counter;
        std::string x_signature;
    };

    OriginBridge(const std::string& origin_url, const std::string& node_id, 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() { if (curl_) curl_easy_cleanup(curl_); }

    OriginBridge(const OriginBridge&) = delete;
    OriginBridge& operator=(const OriginBridge&) = delete;

    SignedRequest 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 = now_epoch();
        uint64_t cnt = counter_.fetch_add(1);
        req.x_timestamp = std::to_string(now);
        req.x_counter = std::to_string(cnt);
        std::string sig_payload;
        sig_payload.reserve(node_id_.size() + method.size() + path.size() + body.size() + 32);
        sig_payload += node_id_;
        sig_payload += method;
        sig_payload += path;
        sig_payload += body;
        sig_payload += req.x_counter;
        sig_payload += req.x_timestamp;
        std::vector<uint8_t> mac = CryptoEngine::hmac_sha256(
            reinterpret_cast<const uint8_t*>(sig_payload.data()), sig_payload.size(), hmac_key_.data());
        req.x_signature = hex_encode(mac.data(), mac.size());
        return req;
    }

    bool verify_response(const std::vector<uint8_t>& data, const uint8_t* hmac) const {
        if (!hmac) return false;
        return CryptoEngine::verify_hmac(data.data(), data.size(), hmac_key_.data(), hmac);
    }

    bool 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());
    }

    bool 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);
        }
        std::vector<uint8_t> mac = CryptoEngine::hmac_sha256(packet.data(), packet.size(), hmac_key_.data());
        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);
    }

    bool send_firewall_rules(const std::vector<FirewallRule>& rules) {
        Json::Value root(Json::arrayValue);
        for (const auto& r : rules) {
            Json::Value 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"] = static_cast<Json::UInt>(r.expire_at);
            root.append(obj);
        }
        Json::FastWriter writer;
        std::string body = writer.write(root);
        auto req = sign_request("POST", "/api/firewall/rules", body);
        return http_post(origin_url_ + req.path, body, req);
    }

    bool send_heartbeat(uint8_t cpu, uint8_t mem, uint8_t disk, uint32_t peers,
                        ServiceState state, NodeRole role) {
        Json::Value root;
        root["node_id"] = node_id_;
        root["timestamp"] = static_cast<Json::UInt>(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;
        Json::FastWriter writer;
        std::string body = writer.write(root);
        auto req = sign_request("POST", "/api/heartbeat", body);
        return http_post(origin_url_ + req.path, body, req);
    }

    OriginCommand 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;
        }
        Json::Value root;
        Json::Reader reader;
        if (!reader.parse(response, root)) {
            LOG(WARN, "OriginBridge::receive_command: JSON parse failed");
            return cmd;
        }
        if (!root.isObject() || !root.isMember("command")) return cmd;
        std::string cmd_str = root["command"].asString();
        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.isMember("seq")) cmd.seq = root["seq"].asUInt();
        if (root.isMember("timestamp")) cmd.timestamp = root["timestamp"].asUInt();
        if (root.isMember("node_id")) cmd.node_id = root["node_id"].asUInt();
        if (root.isMember("payload")) cmd.payload = root["payload"].asString();
        if (root.isMember("hmac")) {
            std::string hmac_hex = root["hmac"].asString();
            std::string verify_payload = cmd_str + std::to_string(cmd.seq) +
                std::to_string(cmd.timestamp) + cmd.payload;
            std::vector<uint8_t> hmac = hex_decode(hmac_hex);
            if (!CryptoEngine::verify_hmac(reinterpret_cast<const uint8_t*>(verify_payload.data()),
                                                verify_payload.size(), hmac_key_.data(), hmac.data())) {
                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;
    }

    bool report_result(uint32_t seq, bool success, const std::string& output, const std::string& error = "") {
        Json::Value root;
        root["node_id"] = node_id_;
        root["seq"] = seq;
        root["success"] = success;
        root["output"] = output;
        if (!error.empty()) root["error"] = error;
        root["timestamp"] = static_cast<Json::UInt>(now_epoch());
        Json::FastWriter writer;
        std::string body = writer.write(root);
        auto req = sign_request("POST", "/api/commands/result", body);
        return http_post(origin_url_ + req.path, body, req);
    }

    bool send_status_report(NodeRole role, ServiceState state, const std::string& details) {
        Json::Value root;
        root["node_id"] = node_id_;
        root["timestamp"] = static_cast<Json::UInt>(now_epoch());
        root["role"] = role_name(role);
        root["state"] = state_name(state);
        root["details"] = details;
        root["version"] = E_VERSION;
        Json::FastWriter writer;
        std::string body = writer.write(root);
        auto req = sign_request("POST", "/api/status", body);
        return http_post(origin_url_ + req.path, body, req);
    }

private:
    std::string origin_url_;
    std::string node_id_;
    CryptoEngine& crypto_ref_;
    CURL* curl_;
    SecureBuffer<32> hmac_key_;
    std::atomic<uint64_t> counter_{1};
    std::atomic<uint32_t> last_seq_{0};
    std::atomic<uint32_t> last_ts_{0};
    static constexpr uint32_t TS_WINDOW_S = 300;

    void 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;
                }
            }
        }
        if (crypto_ref_.is_ready()) {
            std::vector<uint8_t> seed = CryptoEngine::hmac_sha256(
                node_id_, reinterpret_cast<const uint8_t*>("HMAC-DERIVE-2024"));
            std::copy(seed.begin(), seed.begin() + std::min(seed.size(), size_t(32)), hmac_key_.data());
            LOG(WARN, "OriginBridge: using derived HMAC key");
            return;
        }
        throw std::runtime_error("OriginBridge: cannot load HMAC key");
    }

    bool check_replay(uint32_t seq, uint32_t timestamp) {
        uint32_t now = 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;
    }

    static size_t 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* 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 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 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 http_get(const std::string& url, std::string& response, const SignedRequest& req, long* out_http_code = nullptr) {
        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;
    }
};
