#pragma once

#include "common.hpp"
#include <array>
#include <sys/un.h>

struct EventRingEntry {
    SecurityEventPacket packet;
    std::vector<uint8_t> payload;
    uint32_t seq_num = 0;
};

class SecurityEncoder {
public:
    SecurityEncoder(NodeRole node_role, uint32_t node_id)
        : node_role_(node_role), node_id_(node_id), ring_buffer_(RING_SIZE) {}

    ~SecurityEncoder() = default;

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

    std::vector<uint8_t> encode_event(const SecurityEventPacket& event,
                                      const std::vector<uint8_t>& payload,
                                      const uint8_t* key) {
        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()) {
            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);
        }
        append_hmac(packet, key);
        total_events_.fetch_add(1);
        return packet;
    }

    std::pair<SecurityEventPacket, std::vector<uint8_t>> decode_event(const uint8_t* data,
                                                                        size_t len,
                                                                        const uint8_t* key) {
        if (len < sizeof(SecurityEventPacket) + HMAC_LEN) {
            throw std::runtime_error("decode_event: packet too short");
        }
        if (!verify_hmac(data, len, key)) {
            throw std::runtime_error("decode_event: HMAC verification failed");
        }
        SecurityEventPacket pkt;
        std::memcpy(&pkt, data, sizeof(SecurityEventPacket));
        size_t hdr_plus_payload = len - HMAC_LEN;
        uint16_t pLen = pkt.payload_len;
        std::vector<uint8_t> payload;
        if (pLen > 0 && sizeof(SecurityEventPacket) + pLen <= hdr_plus_payload) {
            payload.insert(payload.end(), data + sizeof(SecurityEventPacket),
                           data + sizeof(SecurityEventPacket) + pLen);
        }
        return {pkt, payload};
    }

    bool verify_hmac(const uint8_t* packet, size_t len, const uint8_t* key) {
        if (len < sizeof(SecurityEventPacket) + HMAC_LEN) return false;
        size_t data_len = len - HMAC_LEN;
        const uint8_t* received_hmac = packet + data_len;
        std::array<uint8_t, HMAC_LEN> computed = compute_hmac(packet, data_len, key);
        return sodium_memcmp(computed.data(), received_hmac, HMAC_LEN) == 0;
    }

    bool forward_to_service_agent(const std::vector<uint8_t>& binary_event) {
        int sock = socket(AF_UNIX, SOCK_STREAM, 0);
        if (sock < 0) return false;
        struct sockaddr_un addr;
        std::memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
        std::strncpy(addr.sun_path, "/run/service-agent.sock", sizeof(addr.sun_path) - 1);
        if (connect(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
            close(sock);
            return false;
        }
        ssize_t sent = send(sock, binary_event.data(), binary_event.size(), MSG_NOSIGNAL);
        if (sent != static_cast<ssize_t>(binary_event.size())) {
            close(sock);
            return false;
        }
        bool ack = read_agent_ack(sock);
        close(sock);
        return ack;
    }

    bool push_to_origin(const std::vector<uint8_t>& binary_event) {
        (void)binary_event;
        return false;
    }

    void buffer_event(const SecurityEventPacket& packet, const std::vector<uint8_t>& payload) {
        std::lock_guard<std::mutex> lock(ring_mutex_);
        uint32_t seq = sequence_num_.fetch_add(1);
        ring_buffer_[ring_head_] = {packet, payload, seq};
        ring_head_ = (ring_head_ + 1) % RING_SIZE;
        if (ring_head_ == 0) ring_full_ = true;
    }

    std::vector<EventRingEntry> get_recent_events(size_t count) const {
        std::lock_guard<std::mutex> lock(ring_mutex_);
        size_t total = ring_full_ ? RING_SIZE : ring_head_;
        count = std::min(count, total);
        std::vector<EventRingEntry> result;
        result.reserve(count);
        for (size_t i = 0; i < count; ++i) {
            size_t idx = (ring_head_ + RING_SIZE - 1 - i) % RING_SIZE;
            result.push_back(ring_buffer_[idx]);
        }
        return result;
    }

    uint64_t total_events() const { return total_events_.load(); }

    SecurityEventPacket build_packet(uint8_t event_type, uint8_t severity, uint32_t src_ip,
                                      uint16_t src_port, AttackType attack,
                                      const std::vector<uint8_t>& payload) const {
        SecurityEventPacket pkt;
        pkt.magic[0] = 0x43; pkt.magic[1] = 0x45; pkt.magic[2] = 0x53; pkt.magic[3] = 0x45;
        pkt.timestamp = now_epoch();
        pkt.node_role = static_cast<uint8_t>(node_role_);
        pkt.attack_type = static_cast<uint8_t>(attack);
        pkt.severity = severity;
        pkt.source_port = src_port;
        pkt.source_ip = src_ip;
        pkt.payload_len = static_cast<uint16_t>(std::min(payload.size(), static_cast<size_t>(UINT16_MAX)));
        return pkt;
    }

private:
    NodeRole node_role_;
    uint32_t node_id_;
    std::atomic<uint64_t> total_events_{0};
    std::atomic<uint32_t> sequence_num_{0};

    static constexpr size_t RING_SIZE = 10000;
    mutable std::mutex ring_mutex_;
    std::vector<EventRingEntry> ring_buffer_;
    size_t ring_head_ = 0;
    bool ring_full_ = false;

    std::array<uint8_t, HMAC_LEN> compute_hmac(const uint8_t* data, size_t len, const uint8_t* key) const {
        std::array<uint8_t, HMAC_LEN> mac;
        crypto_auth_hmacsha256(mac.data(), data, len, key);
        return mac;
    }

    void append_hmac(std::vector<uint8_t>& packet, const uint8_t* key) const {
        std::array<uint8_t, HMAC_LEN> mac = compute_hmac(packet.data(), packet.size(), key);
        packet.insert(packet.end(), mac.begin(), mac.end());
    }

    bool read_agent_ack(int sock_fd) const {
        uint8_t ack;
        ssize_t n = recv(sock_fd, &ack, 1, 0);
        return n == 1 && ack == 0x06;
    }
};
