// jtlsrv-cpp — main.cpp  (Milestone 3: Router + pairing + endpoints)

#include <cstdio>
#include <string>
#include <chrono>

#include <uv.h>

#include "config.hpp"
#include "log.hpp"
#include "tls_server.hpp"
#include "http.hpp"
#include "router.hpp"
#include "pairing.hpp"
#include "db/pool.hpp"

#include <vips/vips.h>
#include "queries/shop.hpp"
#include "queries/customer_groups.hpp"
#include "request_log.hpp"
#include "order_log.hpp"

// ---------------------------------------------------------------------------
// Globals
// ---------------------------------------------------------------------------

static uv_loop_t*  loop = nullptr;
static Router      router;
static PairingStore pairing_store;
static RequestLog  request_log;

static json build_config() {
    return {
        {"authToken", config::get("AUTH_TOKEN", "df40ad2067954646abb0499548a52241")},
        {"certificateFingerprint", config::get("CERTIFICATE_FINGERPRINT", "BC2114CF407A42724BEEF417960F76DCBF9DE879")},
        {"certificateSerialNumber", config::get("CERTIFICATE_SERIAL_NUMBER", "00BFC8BEACDB981B165210EF111CB9D3")},
        {"serverFingerprint", config::get("SERVER_FINGERPRINT", "39-6D-BD-DE-F3-5C-5A-EA-C2-19-CF-EB-A7-A9-58-2F-20-3F-20-F7-3D-E6-CA-8E-AE-FD-28-30-37-A6-45-AE")},
        {"mandantId", config::get("MANDANT_ID", "1")},
        {"mandantName", config::get("MANDANT_NAME", "eB-Standard")},
        {"mandantDatabase", config::get("MANDANT_DATABASE", "eazybusiness")},
    };
}

static json server_config;

// ---------------------------------------------------------------------------
// Endpoint declarations (defined in src/endpoints/*.cpp)
// ---------------------------------------------------------------------------

extern void handle_client(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_init(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_category(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_product(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_productcomposite(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_deleted_entity(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_customergroup(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_order(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_pimage(HttpRequest&, HttpResponse&, RouteContext&);
extern void handle_cimage(HttpRequest&, HttpResponse&, RouteContext&);

// ---------------------------------------------------------------------------
// Request handler — dispatches via Router
// ---------------------------------------------------------------------------

static void handle_request(tls_session* sess) {
    auto start = std::chrono::steady_clock::now();
    router.dispatch(sess, pairing_store, server_config);
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now() - start).count();

    auto& req = sess->current_request;
    auto& resp = sess->current_response;
    std::string url = req.path;
    if (!req.query_string.empty()) url += "?" + req.query_string;

    // Build response body for logging
    std::string resp_body;
    if (resp.status_code == 200) {
        // Re-serialize to get size (body already sent, but we can dump from status)
        // We need to capture the body before sending - patch: store it in resp
        resp_body = resp.body_for_log;
    }
    size_t resp_size = resp_body.size();

    // Console log with response size and truncated body
    const char* ip = sess->peer_ip.c_str();
    if (resp_size > 0) {
        std::string preview = resp_body.substr(0, std::min(resp_size, (size_t)220));
        logc::info("%s %s %s %d %dms [%zu bytes] %s",
                   ip, req.method.c_str(), url.c_str(), resp.status_code,
                   (int)elapsed, resp_size, preview.c_str());
    } else {
        logc::info("%s %s %s %d %dms",
                   ip, req.method.c_str(), url.c_str(), resp.status_code, (int)elapsed);
    }

    // Request log
    request_log.log(ip, req.method, url, resp.status_code, (int)elapsed, resp_body);
}

// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------

int main(int /*argc*/, char* argv[]) {
    config::load(".env");

    if (VIPS_INIT(argv[0])) {
        vips_error_exit("unable to init libvips");
    }

    int port = config::get_int("PORT", 4443);
    std::string bind_address = config::get("BIND_ADDRESS", "0.0.0.0");
    std::string cert_path = "certs/cert.pem";
    std::string key_path  = "certs/key.pem";

    loop = uv_default_loop();

    server_config = build_config();

    // Register routes
    router.add_route("GET", "/v1/client", handle_client);
    router.add_route("GET", "/v1/init",   handle_init);
    router.add_route("GET", "/v1/category", handle_category);
    router.add_route("GET", "/v1/product", handle_product);
    router.add_route("GET", "/v1/productcomposite", handle_productcomposite);
    router.add_route("GET", "/v1/deletedentity", handle_deleted_entity);
    router.add_route("GET", "/v1/customergroup", handle_customergroup);
    router.add_route("POST", "/v1/order", handle_order);
    router.add_route("GET", "/v1/pimage", handle_pimage);
    router.add_route("GET", "/v1/cimage", handle_cimage);

    // Initialize pairing store
    pairing_store.set_pairing_code(config::get("PAIRING_CODE", "307018"), "JTL-POS");
    pairing_store.register_device(server_config["authToken"].get<std::string>(), "JTL-POS");

    // Connect to MSSQL
    if (get_pool().connect() == 0) {
        logc::success("MSSQL connected: %s/%s",
            config::get("MSSQL_SERVER").c_str(),
            config::get("MSSQL_DATABASE").c_str());
        if (fetch_active_shop()) {
            logc::info("Active shop ID: %d", get_active_shop_id());
        } else {
            logc::warn("Active shop not loaded — sync filters and order mapping may be wrong");
        }
        (void)get_customer_group_ids();
    } else {
        logc::warn("MSSQL connection skipped");
        logc::warn("POS handshake will still work; sync from database is not available yet.");
    }

    tls_server_set_handler(handle_request);

    // Open log files
    request_log.open(config::get("LOG_FILE", "logs/requests.log"));
    g_order_log.open(config::get("ORDER_LOG_FILE", "logs/orders.log"));

    int r = tls_server_init(loop, bind_address.c_str(), port,
                            cert_path.c_str(), key_path.c_str());
    if (r != 0) {
        logc::error("failed to start TLS server");
        return 1;
    }

    tls_server_install_signals(loop);

    logc::info("pairing code: %s", config::get("PAIRING_CODE", "307018").c_str());

    uv_run(loop, UV_RUN_DEFAULT);

    request_log.close();
    g_order_log.close();
    get_pool().disconnect();
    vips_shutdown();
    logc::info("shutdown complete.");
    return 0;
}
