#include "engine_email.h"

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

namespace node669427 {

std::string EmailDispatcher::extract_json_field(const std::string& json, const std::string& key) {
    auto pos = json.find("\"" + key + "\"");
    if (pos == std::string::npos) return "";
    auto colon = json.find(':', pos);
    if (colon == std::string::npos) return "";
    auto start = json.find_first_of("\"", colon + 1);
    if (start == std::string::npos) {
        auto val_start = json.find_first_not_of(" \t", colon + 1);
        if (val_start == std::string::npos) return "";
        auto val_end = json.find_first_of(",}\r\n", val_start);
        if (val_end == std::string::npos) val_end = json.size();
        return json.substr(val_start, val_end - val_start);
    }
    auto end = json.find_first_of("\"", start + 1);
    if (end == std::string::npos) return "";
    return json.substr(start + 1, end - start - 1);
}

std::string EmailDispatcher::extract_nested_json(const std::string& json, const std::string& key) {
    auto pos = json.find("\"" + key + "\"");
    if (pos == std::string::npos) return "";
    auto colon = json.find(':', pos);
    if (colon == std::string::npos) return "";
    auto start = json.find_first_of('{', colon + 1);
    if (start == std::string::npos) return "";
    size_t depth = 1;
    size_t end = start + 1;
    while (end < json.size() && depth > 0) {
        if (json[end] == '{') depth++;
        if (json[end] == '}') depth--;
        end++;
    }
    return json.substr(start, end - start);
}

EmailDispatcher::EmailDispatcher(NodeConfig& config, VaultPtr vault)
    : config_(config), vault_(vault) {}

std::string EmailDispatcher::generateEmailId() {
    auto now = std::chrono::system_clock::now();
    auto ts = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
    std::ostringstream ss;
    ss << "eml-" << ts << "-" << sent_count_;
    return ss.str();
}

std::string EmailDispatcher::applyTemplate(const std::string& tmpl_name,
                                             const std::unordered_map<std::string, std::string>& vars) {
    std::string tmpl = vault_->get("email_tmpl_" + tmpl_name);
    if (tmpl.empty()) {
        tmpl = "Hello {{name}},\n\nYour order {{order_id}} has been processed.\n\nThank you.";
    }
    std::string result = tmpl;
    for (const auto& [k, v] : vars) {
        std::string placeholder = "{{" + k + "}}";
        size_t pos = 0;
        while ((pos = result.find(placeholder, pos)) != std::string::npos) {
            result.replace(pos, placeholder.size(), v);
            pos += v.size();
        }
    }
    return result;
}

bool EmailDispatcher::sendViaPostfix(const EmailMessage& msg) {
    CURL* curl = curl_easy_init();
    if (!curl) return false;

    std::string smtp_url = "smtp://127.0.0.1:587";
    std::string from_addr = msg.from.empty() ? "engine@localhost" : msg.from;

    std::string payload = "To: " + msg.to + "\r\n" +
                          "From: " + from_addr + "\r\n" +
                          "Subject: " + msg.subject + "\r\n" +
                          "Content-Type: text/plain; charset=UTF-8\r\n" +
                          "\r\n" + msg.body + "\r\n";

    curl_easy_setopt(curl, CURLOPT_URL, smtp_url.c_str());
    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from_addr.c_str());
    struct curl_slist* recipients = curl_slist_append(nullptr, msg.to.c_str());
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
    curl_easy_setopt(curl, CURLOPT_READDATA, &payload);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION,
        [](char* ptr, size_t size, size_t nmemb, std::string* data) -> size_t {
            static size_t pos = 0;
            if (pos >= data->size()) { pos = 0; return 0; }
            size_t len = std::min(size * nmemb, data->size() - pos);
            memcpy(ptr, data->data() + pos, len);
            pos += len;
            return len;
        });
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L);

    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(recipients);
    curl_easy_cleanup(curl);
    return res == CURLE_OK;
}

std::string EmailDispatcher::dispatch(const std::string& body) {
    std::string to = extract_json_field(body, "to");
    std::string from = extract_json_field(body, "from");
    std::string subject = extract_json_field(body, "subject");
    std::string body_text = extract_json_field(body, "body");
    std::string tmpl_name = extract_json_field(body, "template");

    if (to.empty() || subject.empty()) {
        return "{\"error\":\"missing_to_or_subject\"}";
    }

    EmailMessage msg;
    msg.id = generateEmailId();
    msg.to = to;
    msg.from = from;
    msg.subject = subject;
    msg.template_name = tmpl_name;

    if (!tmpl_name.empty()) {
        std::string vars_json = extract_nested_json(body, "variables");
        if (!vars_json.empty()) {
            auto add_var = [&](const std::string& k) {
                std::string v = extract_json_field(vars_json, k);
                if (!v.empty()) msg.variables[k] = v;
            };
            add_var("name");
            add_var("order_id");
            add_var("amount");
            add_var("status");
            add_var("tracking_id");
        }
        msg.body = applyTemplate(tmpl_name, msg.variables);
    } else {
        msg.body = body_text;
    }

    {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        queue_.push(msg);
    }
    condition_.notify_one();

    return "{\"status\":\"queued\",\"email_id\":\"" + msg.id +
           "\",\"to\":\"" + to + "\"}";
}

std::string EmailDispatcher::queueBulk(const std::string& body) {
    return "{\"status\":\"queued\",\"mode\":\"bulk\"}";
}

void EmailDispatcher::run() {
    while (running_.load() && config_.running.load()) {
        std::unique_lock<std::mutex> lock(queue_mutex_);
        condition_.wait_for(lock, std::chrono::seconds(1),
                            [this]() { return !queue_.empty() || !running_.load(); });
        if (!running_.load()) break;
        if (queue_.empty()) continue;

        EmailMessage msg = std::move(queue_.front());
        queue_.pop();
        lock.unlock();

        bool ok = sendViaPostfix(msg);
        {
            std::lock_guard<std::mutex> stats_lock(stats_mutex_);
            if (ok) sent_count_++;
            else failed_count_++;
        }
    }
}

void EmailDispatcher::stop() {
    running_.store(false);
    condition_.notify_all();
}

} // namespace node669427
