#include "engine_security.h"

#include <sstream>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <thread>
#include <iomanip>
#include <ctime>

namespace node669427 {

SecurityEngine::SecurityEngine() {}

void SecurityEngine::recordRequest(const std::string& ip, const std::string& path) {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    total_requests_++;

    auto now = std::chrono::system_clock::now();
    auto& session = sessions_[ip];
    session.ip = ip;
    session.request_count++;
    session.last_seen = now;
    if (session.first_seen == std::chrono::system_clock::time_point{}) {
        session.first_seen = now;
    }
    session.path_history.push_back(path);
    if (session.path_history.size() > 100) {
        session.path_history.erase(session.path_history.begin());
    }

    detectAnomaly(session, path);
}

void SecurityEngine::detectAnomaly(Session& session, const std::string& path) {
    double score_change = 0.0;

    auto path_count = std::count(session.path_history.begin(),
                                  session.path_history.end(), path);
    if (path_count > 20) {
        score_change -= 5.0;
    }

    if (session.request_count > 1000) {
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(
            session.last_seen - session.first_seen).count();
        if (duration > 0) {
            double rps = static_cast<double>(session.request_count) / duration;
            if (rps > 50.0) score_change -= 10.0;
            else if (rps > 20.0) score_change -= 5.0;
        }
    }

    if (path.find("/admin") != std::string::npos ||
        path.find("/.env") != std::string::npos ||
        path.find("/wp-") != std::string::npos ||
        path.find("/config") != std::string::npos) {
        score_change -= 15.0;
    }

    if (path == "/health" || path == "/status") {
        score_change += 0.5;
    }

    session.trust_score = std::clamp(session.trust_score + score_change, 0.0, 100.0);

    if (session.trust_score < 10.0) {
        session.blocked = true;
        session.strikes++;
        blocked_requests_++;
        logEvent("auto_block", session.ip, "high",
                 "trust_score=" + std::to_string(session.trust_score));
    } else if (session.trust_score < 25.0) {
        session.strikes++;
    }
}

void SecurityEngine::addStrike(const std::string& ip) {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    auto it = sessions_.find(ip);
    if (it != sessions_.end()) {
        it->second.strikes++;
        it->second.trust_score -= 5.0;
        if (it->second.trust_score < 0) it->second.trust_score = 0;
    }
}

void SecurityEngine::logEvent(const std::string& type, const std::string& source_ip,
                               const std::string& severity, const std::string& details) {
    std::lock_guard<std::mutex> lock(events_mutex_);
    auto now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    char buf[64];
    std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&t));

    SecurityEvent ev;
    ev.type = type;
    ev.source_ip = source_ip;
    ev.severity = severity;
    ev.timestamp = buf;
    ev.details = details;
    events_.push_back(ev);

    while (events_.size() > MAX_EVENTS) {
        events_.erase(events_.begin());
    }
}

bool SecurityEngine::isBlocked(const std::string& ip) const {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    auto it = sessions_.find(ip);
    return (it != sessions_.end() && it->second.blocked);
}

double SecurityEngine::getTrustScore(const std::string& ip) const {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    auto it = sessions_.find(ip);
    return (it != sessions_.end()) ? it->second.trust_score : 50.0;
}

std::string SecurityEngine::getEvents() const {
    std::lock_guard<std::mutex> lock(events_mutex_);
    std::ostringstream json;
    json << "[";
    size_t limit = std::min(events_.size(), size_t(100));
    for (size_t i = 0; i < limit; ++i) {
        if (i > 0) json << ",";
        json << "{\"type\":\"" << events_[events_.size() - limit + i].type << "\""
             << ",\"source_ip\":\"" << events_[events_.size() - limit + i].source_ip << "\""
             << ",\"severity\":\"" << events_[events_.size() - limit + i].severity << "\""
             << ",\"timestamp\":\"" << events_[events_.size() - limit + i].timestamp << "\"}";
    }
    json << "]";
    return json.str();
}

std::string SecurityEngine::getSessionStatus(const std::string& ip) const {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    auto it = sessions_.find(ip);
    if (it == sessions_.end()) {
        return "{\"ip\":\"" + ip + "\",\"known\":false}";
    }
    std::ostringstream json;
    json << "{\"ip\":\"" << it->second.ip << "\""
         << ",\"trust_score\":" << it->second.trust_score
         << ",\"strikes\":" << it->second.strikes
         << ",\"requests\":" << it->second.request_count
         << ",\"blocked\":" << (it->second.blocked ? "true" : "false") << "}";
    return json.str();
}

SecurityMetrics SecurityEngine::getMetrics() const {
    SecurityMetrics m;
    m.total_requests = total_requests_.load();
    m.blocked_requests = blocked_requests_.load();

    std::lock_guard<std::mutex> lock(sessions_mutex_);
    m.active_sessions = sessions_.size();

    double total = 0.0;
    for (const auto& [ip, s] : sessions_) {
        total += s.trust_score;
    }
    m.avg_trust_score = sessions_.empty() ? 50.0 : total / sessions_.size();
    return m;
}

void SecurityEngine::decayScores() {
    std::lock_guard<std::mutex> lock(sessions_mutex_);
    auto now = std::chrono::system_clock::now();
    std::vector<std::string> to_remove;
    for (auto& [ip, session] : sessions_) {
        auto idle = std::chrono::duration_cast<std::chrono::minutes>(now - session.last_seen).count();
        if (idle > 5) {
            session.trust_score = std::min(session.trust_score + 2.0, 100.0);
        }
        if (idle > 30 && !session.blocked) {
            to_remove.push_back(ip);
        }
    }
    for (const auto& ip : to_remove) {
        sessions_.erase(ip);
    }
}

void SecurityEngine::run() {
    while (running_.load()) {
        decayScores();
        std::this_thread::sleep_for(std::chrono::seconds(30));
    }
}

void SecurityEngine::stop() {
    running_.store(false);
}

} // namespace node669427
