#include <android/log.h>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <jni.h>
#include <pthread.h>
#include <string>
#include <unistd.h>

#define LOG_TAG "Tun2Socks"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

static std::atomic<bool> g_running{false};
static pthread_t g_thread{};
static int g_tun_fd = -1;

#ifdef USE_HEV_TUN2SOCKS
extern "C" {
#include "hev-main.h"
}
#endif

static std::string buildConfig(int mtu, const char *socksHost, int socksPort) {
    char buf[1024];
    snprintf(buf, sizeof(buf),
             "tunnel:\n"
             "  mtu: %d\n"
             "  ipv4: 10.8.0.2\n"
             "socks5:\n"
             "  port: %d\n"
             "  address: %s\n"
             "  udp: 'tcp'\n"  // UDP-in-TCP → удалённый hev-socks5-server через SSH -L
             "misc:\n"
             "  log-level: error\n",
             mtu, socksPort, socksHost);
    return std::string(buf);
}

static void *worker(void *arg) {
    auto *cfgPath = static_cast<std::string *>(arg);
#ifdef USE_HEV_TUN2SOCKS
    LOGI("Starting hev-socks5-tunnel fd=%d", g_tun_fd);
    hev_socks5_tunnel_main(cfgPath->c_str(), g_tun_fd);
#else
    LOGE("Native module built without hev-socks5-tunnel. Clone submodule — see README.md");
    while (g_running.load()) {
        sleep(1);
    }
#endif
    delete cfgPath;
    return nullptr;
}

extern "C" JNIEXPORT jint JNICALL
Java_com_sshproxy_vpn_vpn_Tun2Socks_nativeStart(
    JNIEnv *env,
    jclass,
    jint tunFd,
    jint mtu,
    jstring socksHost,
    jint socksPort,
    jstring configPathJava) {
    if (g_running.load()) {
        return -1;
    }

    const char *hostChars = env->GetStringUTFChars(socksHost, nullptr);
    std::string config = buildConfig(mtu, hostChars, socksPort);
    env->ReleaseStringUTFChars(socksHost, hostChars);

    const char *pathChars = env->GetStringUTFChars(configPathJava, nullptr);
    std::string path(pathChars);
    env->ReleaseStringUTFChars(configPathJava, pathChars);

    FILE *f = fopen(path.c_str(), "w");
    if (!f) {
        LOGE("Cannot write config");
        return -2;
    }
    fwrite(config.data(), 1, config.size(), f);
    fclose(f);

    g_tun_fd = tunFd;
    g_running.store(true);

#ifndef USE_HEV_TUN2SOCKS
    LOGE("hev-socks5-tunnel missing — traffic will not pass through SOCKS");
    return -3;
#endif

    auto *cfgPath = new std::string(path);
    if (pthread_create(&g_thread, nullptr, worker, cfgPath) != 0) {
        g_running.store(false);
        delete cfgPath;
        return -4;
    }
    return 0;
}

extern "C" JNIEXPORT jlongArray JNICALL
Java_com_sshproxy_vpn_vpn_Tun2Socks_nativeGetStats(JNIEnv *env, jclass) {
    jlong values[2] = {0, 0};
#ifdef USE_HEV_TUN2SOCKS
    if (g_running.load()) {
        size_t tx_packets = 0;
        size_t tx_bytes = 0;
        size_t rx_packets = 0;
        size_t rx_bytes = 0;
        hev_socks5_tunnel_stats(&tx_packets, &tx_bytes, &rx_packets, &rx_bytes);
        values[0] = static_cast<jlong>(rx_bytes);
        values[1] = static_cast<jlong>(tx_bytes);
    }
#endif
    jlongArray result = env->NewLongArray(2);
    if (result != nullptr) {
        env->SetLongArrayRegion(result, 0, 2, values);
    }
    return result;
}

extern "C" JNIEXPORT void JNICALL
Java_com_sshproxy_vpn_vpn_Tun2Socks_nativeStop(JNIEnv *, jclass) {
    if (!g_running.exchange(false)) {
        return;
    }
#ifdef USE_HEV_TUN2SOCKS
    hev_socks5_tunnel_quit();
#endif
    pthread_join(g_thread, nullptr);
    g_tun_fd = -1;
}
