/*
 * test_tcp_loopback — in-process TCP acceptor + client, sends 10 frames.
 * Runs only under dev (requires Asio).
 */
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdio>
#include <mutex>
#include <thread>
#include <vector>

#include "net/transport.h"

using namespace voicecat::net;

static int g_failures = 0;

#define CHECK(cond)                                                        \
    do {                                                                   \
        if (!(cond)) {                                                     \
            std::printf("FAIL [%s:%d]: %s\n", __FILE__, __LINE__, #cond); \
            ++g_failures;                                                  \
        }                                                                  \
    } while (0)

int main() {
    constexpr int kFrameCount = 10;
    constexpr uint16_t kPort = 19850;

    std::mutex mtx;
    std::condition_variable cv;
    std::vector<std::vector<uint8_t>> received;
    std::atomic<bool> server_connected{false};

    // Server io_context + acceptor.
    asio::io_context server_io;
    auto work = asio::make_work_guard(server_io);
    std::thread server_thread([&] { server_io.run(); });

    TcpAcceptor acceptor(server_io, kPort, [&](asio::ip::tcp::socket sock) {
        TcpChannelCallbacks cbs;
        cbs.on_frame = [&](std::vector<uint8_t> frame) {
            std::lock_guard<std::mutex> lk(mtx);
            received.push_back(std::move(frame));
            cv.notify_all();
        };
        cbs.on_connected = [&] { server_connected.store(true); };
        auto conn = std::make_shared<TcpServerConn>(std::move(sock), std::move(cbs));
        return conn;
    });
    acceptor.start();

    // Client.
    std::atomic<bool> client_connected{false};
    TcpChannelCallbacks client_cbs;
    client_cbs.on_connected    = [&] { client_connected.store(true); };
    client_cbs.on_connect_error = [](std::error_code ec) {
        std::printf("connect error: %s\n", ec.message().c_str());
    };

    TcpControlChannel client(std::move(client_cbs));
    client.async_connect("127.0.0.1", kPort);

    // Wait for connection.
    auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
    while (!client_connected.load() &&
           std::chrono::steady_clock::now() < deadline) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    CHECK(client_connected.load());

    // Send kFrameCount distinct frames.
    for (int i = 0; i < kFrameCount; ++i) {
        std::vector<uint8_t> payload = {static_cast<uint8_t>(i), 0xAB, 0xCD};
        client.send_frame(payload);
    }

    // Wait for all frames to arrive on the server side.
    {
        std::unique_lock<std::mutex> lk(mtx);
        bool ok = cv.wait_for(lk, std::chrono::seconds(5),
                              [&] { return static_cast<int>(received.size()) >= kFrameCount; });
        CHECK(ok);
    }
    CHECK(static_cast<int>(received.size()) == kFrameCount);
    for (int i = 0; i < kFrameCount && i < static_cast<int>(received.size()); ++i) {
        CHECK(received[i].size() == 3);
        if (!received[i].empty()) CHECK(received[i][0] == static_cast<uint8_t>(i));
    }

    // Clean up.
    client.close();
    acceptor.stop();
    work.reset();
    server_io.stop();
    server_thread.join();

    if (g_failures == 0) {
        std::printf("tcp_loopback: all checks passed\n");
        return 0;
    }
    std::printf("tcp_loopback: %d failure(s)\n", g_failures);
    return 1;
}
