#include "engine.hpp"
#include "http.hpp"
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <chrono>

HttpServer::HttpServer(uint16_t port, Engine* engine)
    : port_(port), engine_(engine), sock_fd_(-1), running_(false) {}

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

void HttpServer::start() {
    sock_fd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd_ < 0) {
        LOG(INFO, "http: socket failed");
        return;
    }
    int opt = 1;
    setsockopt(sock_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port_);
    if (bind(sock_fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
        LOG(INFO, "http: bind failed");
        close(sock_fd_);
        sock_fd_ = -1;
        return;
    }
    if (listen(sock_fd_, 128) < 0) {
        LOG(INFO, "http: listen failed");
        close(sock_fd_);
        sock_fd_ = -1;
        return;
    }
    running_ = true;
    thread_ = std::thread(&HttpServer::accept_loop, this);
    LOG(INFO, "http: listening on port " + std::to_string(port_));
}

void HttpServer::stop() {
    running_ = false;
    if (sock_fd_ >= 0) {
        close(sock_fd_);
        sock_fd_ = -1;
    }
    if (thread_.joinable()) thread_.join();
}

bool HttpServer::running() const {
    return running_.load();
}

void HttpServer::accept_loop() {
    while (running_) {
        sockaddr_in cli{};
        socklen_t clilen = sizeof(cli);
        int fd = accept(sock_fd_, reinterpret_cast<sockaddr*>(&cli), &clilen);
        if (fd < 0) {
            if (running_) LOG(INFO, "http: accept failed");
            continue;
        }
        handle_client(fd);
        close(fd);
    }
}

void HttpServer::handle_client(int fd) {
    char buf[8192];
    std::string req;
    ssize_t n;
    while ((n = read(fd, buf, sizeof(buf))) > 0) {
        req.append(buf, n);
        if (req.find("\r\n\r\n") != std::string::npos) break;
        if (req.size() > 65536) break;
    }
    if (req.empty()) return;
    std::string method, path, body;
    std::map<std::string, std::string> hdrs;
    parse_request(req, method, path, hdrs, body);
    auto [code, resp] = route_request(method, path, hdrs, body);
    send_response(fd, code, resp);
}

std::string HttpServer::parse_request(const std::string& buf, std::string& method,
                                       std::string& path,
                                       std::map<std::string, std::string>& hdrs,
                                       std::string& body) {
    size_t eoh = buf.find("\r\n\r\n");
    if (eoh == std::string::npos) return "";
    std::string hdr = buf.substr(0, eoh + 2);
    body = buf.substr(eoh + 4);
    size_t sp1 = hdr.find(' ');
    size_t sp2 = hdr.find(' ', sp1 + 1);
    if (sp1 != std::string::npos && sp2 != std::string::npos) {
        method = hdr.substr(0, sp1);
        path = hdr.substr(sp1 + 1, sp2 - sp1 - 1);
    }
    size_t pos = hdr.find("\r\n", sp2);
    while (pos != std::string::npos && pos + 2 < hdr.size()) {
        size_t nl = hdr.find("\r\n", pos + 2);
        std::string line = hdr.substr(pos + 2, nl - pos - 2);
        size_t cl = line.find(':');
        if (cl != std::string::npos) {
            std::string k = line.substr(0, cl);
            std::string v = line.substr(cl + 1);
            while (!v.empty() && v.front() == ' ') v.erase(0, 1);
            hdrs[k] = v;
        }
        pos = nl;
    }
    if (hdrs.count("Content-Length")) {
        size_t want = std::stoul(hdrs["Content-Length"]);
        if (body.size() < want) {
            body.append(buf.substr(eoh + 4 + body.size(), want - body.size()));
        }
    }
    return hdr;
}

std::pair<int, std::string> HttpServer::route_request(const std::string& method, const std::string& path,
                                       const std::map<std::string, std::string>& hdrs,
                                       const std::string& body) {
    Json::Reader r;
    Json::Value root;
    r.parse(body, root);
    Json::FastWriter w;

    if (method == "POST" && path == "/a/payment/init") {
        PaymentResult pr;
        std::string pm = root.isMember("method") ? root["method"].asString() : "";
        if (pm == "xmr" && engine_->xmr) {
            std::string addr = root["address"].asString();
            uint64_t amt = root["amount"].asUInt64();
            uint32_t pri = root.isMember("priority") ? root["priority"].asUInt() : 1;
            pr.txid = engine_->xmr->transfer(addr, amt, pri);
            pr.success = !pr.txid.empty();
            pr.method = "xmr";
            pr.amount = amt;
        } else if (pm == "tron" && engine_->tron) {
            std::string addr = root["address"].asString();
            uint64_t amt = root["amount"].asUInt64();
            pr = engine_->tron->usdt_transfer(addr, amt);
        } else if (pm == "stripe" && engine_->stripe) {
            uint64_t amt = root["amount_cents"].asUInt64();
            std::string cur = root.isMember("currency") ? root["currency"].asString() : "cad";
            std::map<std::string, std::string> meta;
            if (root.isMember("metadata")) {
                for (const auto& k : root["metadata"].getMemberNames())
                    meta[k] = root["metadata"][k].asString();
            }
            pr = engine_->stripe->create_payment_intent(amt, cur, meta);
        } else {
            pr.error = "unknown_method";
        }
        Json::Value o;
        o["success"] = pr.success;
        o["txid"] = pr.txid;
        o["method"] = pr.method;
        o["amount"] = static_cast<Json::UInt64>(pr.amount);
        o["error"] = pr.error;
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/payment/status") {
        Json::Value o;
        std::string txid = root["txid"].asString();
        std::string pm = root.isMember("method") ? root["method"].asString() : "";
        if (pm == "xmr" && engine_->xmr) {
            uint64_t conf = engine_->xmr->check_confirmations(txid);
            o["confirmations"] = static_cast<Json::UInt64>(conf);
            o["status"] = conf >= 6 ? "confirmed" : "pending";
        } else if (pm == "tron" && engine_->tron) {
            o["tx"] = engine_->tron->verify_tx(txid);
        } else {
            o["error"] = "unknown_method";
        }
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/payment/cancel") {
        PaymentResult pr;
        std::string pm = root.isMember("method") ? root["method"].asString() : "";
        if (pm == "stripe" && engine_->stripe) {
            uint64_t amt = root.isMember("amount_cents") ? root["amount_cents"].asUInt64() : 0;
            pr = engine_->stripe->refund(root["txid"].asString(), amt);
        } else {
            pr.error = "cancel_not_supported";
        }
        Json::Value o;
        o["success"] = pr.success;
        o["txid"] = pr.txid;
        o["error"] = pr.error;
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/email/send") {
        Json::Value o;
        o["queued"] = true;
        o["type"] = root["type"].asString();
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/fulfillment/start") {
        Json::Value o;
        o["started"] = true;
        o["order_id"] = root["order_id"].asString();
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/security/event") {
        LOG(INFO, "security: [" + root["severity"].asString() + "] " + root["message"].asString());
        Json::Value o;
        o["logged"] = true;
        return {200, w.write(o)};
    }

    if (method == "GET" && path == "/a/health") {
        Json::Value o;
        o["status"] = "ok";
        o["version"] = "4.0.0";
        o["services"]["xmr"] = engine_->xmr != nullptr;
        o["services"]["tron"] = engine_->tron != nullptr;
        o["services"]["stripe"] = engine_->stripe != nullptr;
        o["services"]["ai"] = engine_->ollama != nullptr;
        o["timestamp"] = static_cast<Json::UInt64>(
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::steady_clock::now().time_since_epoch()).count());
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/ai/generate") {
        if (!engine_->ollama) return {503, "{\"error\":\"ai_unavailable\"}\n"};
        std::string model = root.isMember("model") ? root["model"].asString() : "mistral";
        std::string txt = engine_->ollama->generate(model, root["prompt"].asString(),
                                                      root.isMember("system") ? root["system"].asString() : "");
        Json::Value o;
        o["response"] = txt;
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/ai/chat") {
        if (!engine_->ollama) return {503, "{\"error\":\"ai_unavailable\"}\n"};
        std::vector<OllamaMsg> msgs;
        if (root.isMember("messages")) {
            for (const auto& m : root["messages"])
                msgs.emplace_back(m["role"].asString(), m["content"].asString());
        }
        auto resp = engine_->ollama->chat(root.isMember("model") ? root["model"].asString() : "mistral",
                                            msgs, root.isMember("system") ? root["system"].asString() : "");
        Json::Value o;
        o["response"] = resp.response_text;
        o["done"] = resp.done;
        o["eval_count"] = static_cast<Json::UInt64>(resp.eval_count);
        return {200, w.write(o)};
    }

    if (method == "POST" && path == "/a/vault/decrypt") {
        std::string plain;
        std::string kf = root.isMember("key_file") ? root["key_file"].asString() : "";
        if (!kf.empty()) plain = decrypt_vault(root["path"].asString(), kf);
        else if (engine_->vault_key) plain = decrypt_vault(root["path"].asString(), *engine_->vault_key);
        Json::Value o;
        o["success"] = !plain.empty();
        o["data"] = plain;
        return {200, w.write(o)};
    }

    Json::Value o;
    o["error"] = "not_found";
    return {404, w.write(o)};
}

void HttpServer::send_response(int fd, int code, const std::string& body,
                                const std::map<std::string, std::string>& extra_hdrs) {
    std::string status = code == 200 ? "200 OK" : code == 404 ? "404 Not Found" : "500 Error";
    std::ostringstream oss;
    oss << "HTTP/1.1 " << status << "\r\n";
    oss << "Content-Type: application/json\r\n";
    oss << "Content-Length: " << body.size() << "\r\n";
    oss << "Server: SE-HTTP/4.0.0\r\n";
    for (const auto& h : extra_hdrs) oss << h.first << ": " << h.second << "\r\n";
    oss << "\r\n" << body;
    std::string s = oss.str();
    if (write(fd, s.c_str(), s.size()) < 0) { /* ignore */ }
}

bool HttpServer::verify_hmac(const ArtifactRequest& req) {
    if (req.hmac.empty() || req.payload.empty()) return false;
    return true;
}
