#include "ipc/shared_memory.hpp"
#include <atomic>
#include <chrono>
#include <cstring>
#include <iostream>

uint64_t SharedMemoryManager::NowMs() {
    return static_cast<uint64_t>(
        std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count()
    );
}

TelemetryPacket* SharedMemoryManager::SlotPtr(uint32_t slot) const {
    return reinterpret_cast<TelemetryPacket*>(
        static_cast<uint8_t*>(m_pView) + TELEMETRY_SLOT0_OFFSET +
        static_cast<size_t>(slot) * TELEMETRY_SLOT_STRIDE
    );
}

CommandPacket* SharedMemoryManager::CmdSlotPtr(uint64_t index) const {
    return reinterpret_cast<CommandPacket*>(
        static_cast<uint8_t*>(m_pView) + COMMAND_QUEUE_OFFSET +
        (index % COMMAND_QUEUE_CAPACITY) * COMMAND_SLOT_STRIDE
    );
}

SharedMemoryManager::SharedMemoryManager() = default;

SharedMemoryManager::~SharedMemoryManager() {
    Shutdown();
}

bool SharedMemoryManager::Initialize(bool asServer, const char* mappingName) {
    m_isServer = asServer;
    m_name = mappingName;

    if (m_isServer) {
        m_hMapFile = CreateFileMappingA(
            INVALID_HANDLE_VALUE,
            nullptr,
            PAGE_READWRITE,
            0,
            SHARED_MEM_SIZE,
            mappingName
        );

        if (!m_hMapFile) {
            std::cerr << "[SHM Error] CreateFileMappingA thất bại. Lỗi: " << GetLastError() << std::endl;
            return false;
        }
    } else {
        m_hMapFile = OpenFileMappingA(
            FILE_MAP_ALL_ACCESS,
            FALSE,
            mappingName
        );

        if (!m_hMapFile) {
            std::cerr << "[SHM Error] OpenFileMappingA thất bại. Lỗi: " << GetLastError() << std::endl;
            return false;
        }
    }

    m_pView = MapViewOfFile(
        m_hMapFile,
        FILE_MAP_ALL_ACCESS,
        0,
        0,
        SHARED_MEM_SIZE
    );

    if (!m_pView) {
        std::cerr << "[SHM Error] MapViewOfFile thất bại. Lỗi: " << GetLastError() << std::endl;
        CloseHandle(m_hMapFile);
        m_hMapFile = nullptr;
        return false;
    }

    m_pHeader = reinterpret_cast<SharedMemoryHeader*>(
        static_cast<uint8_t*>(m_pView) + HEADER_OFFSET
    );

    if (m_isServer) {
        // Dọn sạch vùng header, 2 slot telemetry và vùng hàng đợi lệnh
        std::memset(m_pView, 0, TELEMETRY_SLOT0_OFFSET);
        std::memset(SlotPtr(0), 0, TELEMETRY_SLOT_STRIDE * TELEMETRY_SLOT_COUNT);
        std::memset(
            static_cast<uint8_t*>(m_pView) + COMMAND_QUEUE_OFFSET, 0,
            static_cast<size_t>(COMMAND_QUEUE_CAPACITY) * COMMAND_SLOT_STRIDE
        );

        // Khởi tạo header (ghi thường; chỉ Client biết đọc sau khi magic hợp lệ)
        m_pHeader->magic = SHARED_MEM_MAGIC;
        m_pHeader->protocolVersion = PROTOCOL_VERSION;
        m_pHeader->isRunning = 1;
        std::atomic_ref<uint64_t>(m_pHeader->telemetrySequence).store(0, std::memory_order_relaxed);
        std::atomic_ref<uint64_t>(m_pHeader->commandSequence).store(0, std::memory_order_relaxed);
        std::atomic_ref<uint64_t>(m_pHeader->commandReadIndex).store(0, std::memory_order_relaxed);
        std::atomic_ref<uint64_t>(m_pHeader->lastAgentHeartbeat).store(0, std::memory_order_relaxed);
        UpdateHeartbeat();
    } else {
        // Xác thực header từ phía Client trước khi sử dụng
        if (m_pHeader->magic != SHARED_MEM_MAGIC ||
            m_pHeader->protocolVersion != PROTOCOL_VERSION) {
            std::cerr << "[SHM Error] Header không hợp lệ (magic/version không khớp)." << std::endl;
            Shutdown();
            return false;
        }
        UpdateHeartbeat(); // Đánh dấu Agent online ngay khi kết nối
    }

    std::cout << "[SHM] Đã ánh xạ thành công khối nhớ " << (SHARED_MEM_SIZE / (1024 * 1024))
              << " MB (" << m_name << ", "
              << (m_isServer ? "Server/Core" : "Client/Agent") << ")" << std::endl;
    return true;
}

void SharedMemoryManager::Shutdown() {
    if (m_pHeader && m_isServer) {
        m_pHeader->isRunning = 0;
    }

    m_pHeader = nullptr;

    if (m_pView) {
        UnmapViewOfFile(m_pView);
        m_pView = nullptr;
    }

    if (m_hMapFile) {
        CloseHandle(m_hMapFile);
        m_hMapFile = nullptr;
    }
}

// ==========================================================
// TELEMETRY - Seqlock double-buffer (2 slot ping-pong)
// ==========================================================
bool SharedMemoryManager::WriteTelemetry(const TelemetryPacket& packet) {
    if (!m_pHeader) return false;

    // 1. Chọn slot KHÔNG đang được publish để ghi (tránh phá dữ liệu đang đọc)
    const uint64_t currentSeq = std::atomic_ref<uint64_t>(m_pHeader->telemetrySequence)
                                    .load(std::memory_order_relaxed);
    const uint32_t targetSlot = static_cast<uint32_t>((currentSeq + 1) & 1);

    // 2. Ghi dữ liệu rồi MỚI publish (release) -> độc giả không bao giờ thấy
    //    sequence mới kèm dữ liệu cũ (torn-read)
    std::memcpy(SlotPtr(targetSlot), &packet, sizeof(TelemetryPacket));
    std::atomic_ref<uint64_t>(m_pHeader->telemetrySequence)
        .store(currentSeq + 1, std::memory_order_release);

    UpdateHeartbeat();
    return true;
}

bool SharedMemoryManager::ReadTelemetry(TelemetryPacket& outPacket, int maxRetries) const {
    if (!m_pHeader) return false;

    auto seq = std::atomic_ref<uint64_t>(m_pHeader->telemetrySequence);

    for (int attempt = 0; attempt < maxRetries; ++attempt) {
        const uint64_t s1 = seq.load(std::memory_order_acquire);
        if (s1 == 0) return false; // Chưa có snapshot nào được publish

        // Dữ liệu hợp lệ nằm ở slot (s1 & 1) - luôn khác slot writer đang ghi
        std::memcpy(&outPacket, SlotPtr(static_cast<uint32_t>(s1 & 1)), sizeof(TelemetryPacket));

        // Xác nhận writer chưa publish thêm lần nào trong lúc copy
        const uint64_t s2 = seq.load(std::memory_order_acquire);
        if (s1 == s2) return true;
        // Ngược lại: retry (hiếm khi xảy ra vì writer dùng slot kia)
    }

    return false; // Quá số lần retry (writer bận cực kỳ dày đặc)
}

// ==========================================================
// COMMAND QUEUE - Hàng đợi vòng SPSC (Agent produce, Core consume)
// ==========================================================
bool SharedMemoryManager::PublishCommand(const CommandPacket& cmd) {
    if (!m_pHeader) return false;

    auto writeCursor = std::atomic_ref<uint64_t>(m_pHeader->commandSequence);
    auto readCursor  = std::atomic_ref<uint64_t>(m_pHeader->commandReadIndex);

    const uint64_t w = writeCursor.load(std::memory_order_relaxed);
    const uint64_t r = readCursor.load(std::memory_order_acquire);

    // Hàng đợi đầy: producer phải chờ consumer rút bớt (không ghi đè)
    if (w - r >= COMMAND_QUEUE_CAPACITY) return false;

    std::memcpy(CmdSlotPtr(w), &cmd, sizeof(CommandPacket));
    writeCursor.store(w + 1, std::memory_order_release);
    return true;
}

bool SharedMemoryManager::FetchNextCommand(CommandPacket& outCmd) {
    if (!m_pHeader) return false;

    auto writeCursor = std::atomic_ref<uint64_t>(m_pHeader->commandSequence);
    auto readCursor  = std::atomic_ref<uint64_t>(m_pHeader->commandReadIndex);

    const uint64_t r = readCursor.load(std::memory_order_relaxed);
    const uint64_t w = writeCursor.load(std::memory_order_acquire);

    if (r == w) return false; // Hàng đợi trống

    std::memcpy(&outCmd, CmdSlotPtr(r), sizeof(CommandPacket));
    readCursor.store(r + 1, std::memory_order_release);
    return true;
}

uint64_t SharedMemoryManager::PendingCommandCount() const {
    if (!m_pHeader) return 0;
    const uint64_t w = std::atomic_ref<uint64_t>(m_pHeader->commandSequence).load(std::memory_order_acquire);
    const uint64_t r = std::atomic_ref<uint64_t>(m_pHeader->commandReadIndex).load(std::memory_order_relaxed);
    return w - r;
}

// ==========================================================
// NHỊP TIM & WATCHDOG
// ==========================================================
void SharedMemoryManager::UpdateHeartbeat() {
    if (!m_pHeader) return;

    const uint64_t now = NowMs();
    if (m_isServer) {
        std::atomic_ref<uint64_t>(m_pHeader->lastCoreHeartbeat).store(now, std::memory_order_release);
    } else {
        std::atomic_ref<uint64_t>(m_pHeader->lastAgentHeartbeat).store(now, std::memory_order_release);
    }
}

SharedMemoryManager::AgentWatchdogStatus SharedMemoryManager::CheckAgentWatchdog(uint64_t timeoutMs) const {
    if (!m_pHeader) return AgentWatchdogStatus::NoAgentEverConnected;

    const uint64_t last = std::atomic_ref<uint64_t>(m_pHeader->lastAgentHeartbeat)
                              .load(std::memory_order_acquire);
    if (last == 0) return AgentWatchdogStatus::NoAgentEverConnected;

    return (NowMs() - last > timeoutMs)
        ? AgentWatchdogStatus::TimedOut
        : AgentWatchdogStatus::Healthy;
}

bool SharedMemoryManager::IsRunning() const {
    return m_pHeader && (m_pHeader->isRunning == 1);
}

void SharedMemoryManager::RequestStop() {
    if (m_pHeader) {
        m_pHeader->isRunning = 0;
    }
}
