#include "engine_http.h"

#include <iostream>
#include <sstream>
#include <cstring>
#include <ctime>
#include <curl/curl.h>
#include <chrono>

namespace node669427 {

ThreadPool::ThreadPool(size_t threads) {
    for (size_t i = 0; i < threads; ++i) {
        workers_.emplace_back([this]() {
            while (running_.load()) {
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> lock(queue_mutex_);
                    condition_.wait(lock, [this]() { return !tasks_.empty() || !running_.load(); });
                    if (!running_.load() && tasks_.empty()) return;
                    task = std::move(tasks_.front());
                    tasks_.pop();
                }
                if (task) task();
            }
        });
    }
}

ThreadPool::~ThreadPool() {
    stop();
}

void ThreadPool::enqueue(std::function<void()> task) {
    {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        if (!running_.load()) return;
        tasks_.push(std::move(task));
    }
    condition_.notify_one();
}

void ThreadPool::stop() {
    running_.store(false);
    condition_.notify_all();
    for (auto& w : workers_) {
        if (w.joinable()) w.join();
    }
}

static size_t curl_write_cb(void* contents, size_t size, size_t nmemb, std::string* userp) {
    userp->append((char*)contents, size * nmemb);
    return size * nmemb;
}

HttpServer::HttpServer(NodeConfig& config, CryptoEngine& crypto, VaultPtr vault,
                       SecurityEngine& security, ArtifactProcessor& artifacts,
                       PaymentProcessor& payments, EmailDispatcher& email,
                       FulfillmentEngine& fulfillment)
    : config_(config), crypto_(crypto), vault_(vault), security_(security),
      artifacts_(artifacts), payments_(payments), email_(email), fulfillment_(fulfillment) {}

HttpServer::~HttpServer() {
    if (server_fd_ >= 0) close(server_fd_);
}

bool HttpServer::start() {
    thread_pool_ = std::make_unique<ThreadPool>(config_.thread_pool_size);
    return true;
}

void HttpServer::stop() {
    running_.store(false);
    if (server_fd_ >= 0) {
        close(server_fd_);
        server_fd_ = -1;
    }
    if (thread_pool_) thread_pool_->stop();
}

std::string HttpServer::parse_method(const std::string& request) {
    auto space = request.find(' ');
    return (space != std::string::npos) ? request.substr(0, space) : "";
}

std::string HttpServer::parse_path(const std::string& request) {
    auto first = request.find(' ');
    if (first == std::string::npos) return "";
    auto second = request.find(' ', first + 1);
    if (second == std::string::npos) return "";
    return request.substr(first + 1, second - first - 1);
}

std::string HttpServer::parse_body(const std::string& request) {
    auto header_end = request.find("\r\n\r\n");
    if (header_end == std::string::npos) return "";
    return request.substr(header_end + 4);
}

std::unordered_map<std::string, std::string> HttpServer::parse_headers(const std::string& request) {
    std::unordered_map<std::string, std::string> headers;
    auto header_end = request.find("\r\n\r\n");
    if (header_end == std::string::npos) return headers;
    std::string header_section = request.substr(0, header_end);
    std::istringstream iss(header_section);
    std::string line;
    std::getline(iss, line);
    while (std::getline(iss, line)) {
        if (line.back() == '\r') line.pop_back();
        auto colon = line.find(':');
        if (colon == std::string::npos) continue;
        std::string key = line.substr(0, colon);
        std::string val = line.substr(colon + 1);
        auto start = val.find_first_not_of(" \t");
        if (start != std::string::npos) val = val.substr(start);
        for (auto& c : key) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
        headers[key] = val;
    }
    return headers;
}

std::string HttpServer::extract_path_param(const std::string& path, size_t segment) {
    std::vector<std::string> parts;
    std::istringstream iss(path);
    std::string part;
    while (std::getline(iss, part, '/')) {
        if (!part.empty()) parts.push_back(part);
    }
    return (segment < parts.size()) ? parts[segment] : "";
}

void HttpServer::send_response(int client_fd, int status, const std::string& body) {
    const char* status_text = "OK";
    switch (status) {
        case 200: status_text = "OK"; break;
        case 201: status_text = "Created"; break;
        case 400: status_text = "Bad Request"; break;
        case 401: status_text = "Unauthorized"; break;
        case 403: status_text = "Forbidden"; break;
        case 404: status_text = "Not Found"; break;
        case 500: status_text = "Internal Server Error"; break;
    }
    std::ostringstream response;
    response << "HTTP/1.1 " << status << " " << status_text << "\r\n"
             << "Content-Type: application/json\r\n"
             << "Content-Length: " << body.size() << "\r\n"
             << "Connection: close\r\n"
             << "X-Engine-Node: " << config_.node_id << "\r\n"
             << "\r\n" << body;
    std::string resp = response.str();
    send(client_fd, resp.c_str(), resp.size(), 0);
}

void HttpServer::handle_client(int client_fd, const std::string& client_ip) {
    char buffer[8192] = {};
    ssize_t bytes = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
    if (bytes <= 0) {
        close(client_fd);
        return;
    }
    std::string request(buffer, bytes);
    HttpRequest req;
    req.method = parse_method(request);
    req.path = parse_path(request);
    req.body = parse_body(request);
    req.headers = parse_headers(request);
    req.client_ip = client_ip;

    security_.recordRequest(client_ip, req.path);

    std::string response = route_request(req);
    int status = (response.find("\"error\"") != std::string::npos) ? 400 : 200;
    if (response.find("\"error\":\"not_found\"") != std::string::npos) status = 404;
    if (response.find("\"error\":\"unauthorized\"") != std::string::npos) status = 401;

    send_response(client_fd, status, response);
    close(client_fd);
}

std::string HttpServer::route_request(const HttpRequest& req) {
    if (req.method == "POST" && req.path == "/artifact") {
        if (!validate_hmac(req)) return "{\"error\":\"unauthorized\",\"reason\":\"invalid_hmac\"}";
        return handle_artifact(req);
    }
    if (req.method == "GET" && req.path == "/health") return handle_health(req);
    if (req.method == "GET" && req.path == "/status") return handle_status(req);
    if (req.method == "POST" && req.path == "/payment/init") return handle_payment_init(req);
    if (req.method == "GET" && req.path.find("/payment/status/") == 0) return handle_payment_status(req);
    if (req.method == "POST" && req.path == "/email/send") return handle_email_send(req);
    if (req.method == "POST" && req.path == "/fulfillment/start") return handle_fulfillment_start(req);
    if (req.method == "GET" && req.path == "/security/events") return handle_security_events(req);
    if (req.method == "POST" && req.path == "/ai/infer") {
        if (!validate_hmac(req)) return "{\"error\":\"unauthorized\",\"reason\":\"invalid_hmac\"}";
        return handle_ai_infer(req);
    }
    return "{\"error\":\"not_found\",\"path\":\"" + req.path + "\"}";
}

bool HttpServer::validate_hmac(const HttpRequest& req) const {
    auto ts_it = req.headers.find("x-engine-ts");
    auto nonce_it = req.headers.find("x-engine-nonce");
    auto sig_it = req.headers.find("x-engine-sig");
    if (ts_it == req.headers.end() || nonce_it == req.headers.end() || sig_it == req.headers.end()) {
        return false;
    }
    // CRY-02 canonical: HMAC-SHA256( ts.nonce.METHOD.path.body ) — replay-protected
    std::string data = ts_it->second + "." + nonce_it->second + "." +
                       req.method + "." + req.path + "." + req.body;
    std::string expected = crypto_.hmacSha256(data);
    if (expected.length() != sig_it->second.length()) return false;
    unsigned char result = 0;
    for (size_t i = 0; i < expected.length(); ++i) {
        result |= (expected[i] ^ sig_it->second[i]);
    }
    return result == 0;
}

std::string HttpServer::handle_artifact(const HttpRequest& req) {
    return artifacts_.process(req.body);
}

std::string HttpServer::handle_health(const HttpRequest& req) {
    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));
    return "{\"status\":\"ok\",\"node\":\"" + config_.node_id +
           "\",\"role\":\"" + config_.role +
           "\",\"time\":\"" + buf +
           "\",\"uptime\":\"running\"}";
}

std::string HttpServer::handle_status(const HttpRequest& req) {
    auto metrics = security_.getMetrics();
    std::ostringstream json;
    json << "{\"node\":\"" << config_.node_id << "\""
         << ",\"role\":\"" << config_.role << "\""
         << ",\"listen_port\":" << config_.listen_port
         << ",\"shield\":\"" << config_.shield_ip << "\""
         << ",\"shield_port\":" << config_.shield_port
         << ",\"vault_loaded\":" << (vault_->size() > 0 ? "true" : "false")
         << ",\"vault_entries\":" << vault_->size()
         << ",\"requests_total\":" << metrics.total_requests
         << ",\"requests_blocked\":" << metrics.blocked_requests
         << ",\"active_sessions\":" << metrics.active_sessions
         << ",\"trust_score_avg\":" << metrics.avg_trust_score
         << "}";
    return json.str();
}

std::string HttpServer::handle_payment_init(const HttpRequest& req) {
    return payments_.initiate(req.body);
}

std::string HttpServer::handle_payment_status(const HttpRequest& req) {
    std::string id = extract_path_param(req.path, 2);
    return payments_.status(id);
}

std::string HttpServer::handle_email_send(const HttpRequest& req) {
    return email_.dispatch(req.body);
}

std::string HttpServer::handle_fulfillment_start(const HttpRequest& req) {
    return fulfillment_.start(req.body);
}

std::string HttpServer::handle_security_events(const HttpRequest& req) {
    return security_.getEvents();
}

std::string HttpServer::handle_ai_infer(const HttpRequest& req) {
    std::string ollama_url = config_.services.ollama_url;
    if (ollama_url.empty()) {
        return "{\"error\":\"ollama_not_configured\"}";
    }

    CURL* curl = curl_easy_init();
    if (!curl) return "{\"error\":\"curl_init_failed\"}";

    std::string url = ollama_url + "/api/generate";
    std::string response;
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.body.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);

    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        return "{\"error\":\"proxy_failed\",\"detail\":\"" + std::string(curl_easy_strerror(res)) + "\"}";
    }
    return response;
}

void HttpServer::run() {
    server_fd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd_ < 0) {
        std::cerr << "[HttpServer] socket() failed" << std::endl;
        return;
    }
    int opt = 1;
    setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(config_.listen_port);
    addr.sin_addr.s_addr = inet_addr(config_.bind_address.c_str());

    if (bind(server_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        std::cerr << "[HttpServer] bind() failed on port " << config_.listen_port << std::endl;
        return;
    }

    listen(server_fd_, 64);
    running_.store(true);
    std::cout << "[HttpServer] Listening on " << config_.bind_address << ":" << config_.listen_port << std::endl;

    while (running_.load() && config_.running.load()) {
        struct sockaddr_in client_addr{};
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd_, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            if (!running_.load()) break;
            continue;
        }
        std::string client_ip = inet_ntoa(client_addr.sin_addr);
        thread_pool_->enqueue([this, client_fd, client_ip]() {
            handle_client(client_fd, client_ip);
        });
    }

    close(server_fd_);
    server_fd_ = -1;
}

} // namespace node669427
