#include "audit_log.h"

#include <algorithm>

namespace mosis {

//=============================================================================
// CONSTRUCTOR
//=============================================================================

AuditLog::AuditLog(size_t max_entries)
    : m_max_entries(max_entries)
{
    m_entries.resize(max_entries);
}

//=============================================================================
// LOGGING
//=============================================================================

void AuditLog::Log(AuditEvent event, const std::string& app_id,
                   const std::string& details, bool success) {
    std::lock_guard<std::mutex> lock(m_mutex);

    AuditEntry entry{
        .timestamp = std::chrono::system_clock::now(),
        .event = event,
        .app_id = app_id,
        .details = details,
        .success = success
    };

    m_entries[m_write_index] = std::move(entry);
    m_write_index = (m_write_index + 1) % m_max_entries;
    m_total_logged++;

    if (m_total_logged > m_max_entries) {
        m_wrapped = true;
    }
}

//=============================================================================
// QUERIES
//=============================================================================

std::vector<AuditEntry> AuditLog::GetEntries(size_t count) const {
    std::lock_guard<std::mutex> lock(m_mutex);

    std::vector<AuditEntry> result;
    size_t stored = GetStoredEntries();
    count = std::min(count, stored);

    result.reserve(count);

    // Read from most recent backwards
    for (size_t i = 0; i < count; i++) {
        size_t idx = (m_write_index + m_max_entries - 1 - i) % m_max_entries;
        result.push_back(m_entries[idx]);
    }

    return result;
}

std::vector<AuditEntry> AuditLog::GetEntriesForApp(const std::string& app_id,
                                                    size_t count) const {
    std::lock_guard<std::mutex> lock(m_mutex);

    std::vector<AuditEntry> result;
    result.reserve(count);

    size_t stored = GetStoredEntries();

    for (size_t i = 0; i < stored && result.size() < count; i++) {
        size_t idx = (m_write_index + m_max_entries - 1 - i) % m_max_entries;
        if (m_entries[idx].app_id == app_id) {
            result.push_back(m_entries[idx]);
        }
    }

    return result;
}

std::vector<AuditEntry> AuditLog::GetEntriesByEvent(AuditEvent event,
                                                     size_t count) const {
    std::lock_guard<std::mutex> lock(m_mutex);

    std::vector<AuditEntry> result;
    result.reserve(count);

    size_t stored = GetStoredEntries();

    for (size_t i = 0; i < stored && result.size() < count; i++) {
        size_t idx = (m_write_index + m_max_entries - 1 - i) % m_max_entries;
        if (m_entries[idx].event == event) {
            result.push_back(m_entries[idx]);
        }
    }

    return result;
}

//=============================================================================
// STATISTICS
//=============================================================================

size_t AuditLog::GetTotalEntries() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_total_logged;
}

size_t AuditLog::GetStoredEntries() const {
    // Note: caller should hold lock
    if (m_wrapped) {
        return m_max_entries;
    }
    return m_write_index;
}

size_t AuditLog::CountEvents(AuditEvent event, const std::string& app_id) const {
    std::lock_guard<std::mutex> lock(m_mutex);

    size_t count = 0;
    size_t stored = GetStoredEntries();

    for (size_t i = 0; i < stored; i++) {
        const auto& entry = m_entries[i];
        if (entry.event == event) {
            if (app_id.empty() || entry.app_id == app_id) {
                count++;
            }
        }
    }

    return count;
}

//=============================================================================
// CLEAR
//=============================================================================

void AuditLog::Clear() {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_write_index = 0;
    m_total_logged = 0;
    m_wrapped = false;
    // Clear all entries
    for (auto& entry : m_entries) {
        entry = AuditEntry{};
    }
}

//=============================================================================
// UTILITIES
//=============================================================================

const char* AuditLog::EventToString(AuditEvent event) {
    switch (event) {
        case AuditEvent::AppStart: return "AppStart";
        case AuditEvent::AppStop: return "AppStop";
        case AuditEvent::PermissionCheck: return "PermissionCheck";
        case AuditEvent::PermissionGranted: return "PermissionGranted";
        case AuditEvent::PermissionDenied: return "PermissionDenied";
        case AuditEvent::NetworkRequest: return "NetworkRequest";
        case AuditEvent::NetworkBlocked: return "NetworkBlocked";
        case AuditEvent::FileAccess: return "FileAccess";
        case AuditEvent::FileBlocked: return "FileBlocked";
        case AuditEvent::DatabaseAccess: return "DatabaseAccess";
        case AuditEvent::CameraAccess: return "CameraAccess";
        case AuditEvent::MicrophoneAccess: return "MicrophoneAccess";
        case AuditEvent::LocationAccess: return "LocationAccess";
        case AuditEvent::SandboxViolation: return "SandboxViolation";
        case AuditEvent::ResourceLimitHit: return "ResourceLimitHit";
        case AuditEvent::RateLimitHit: return "RateLimitHit";
        case AuditEvent::Custom: return "Custom";
        default: return "Unknown";
    }
}

//=============================================================================
// GLOBAL INSTANCE
//=============================================================================

AuditLog& GetAuditLog() {
    static AuditLog instance(10000);
    return instance;
}

} // namespace mosis
