import { dlopen, ptr, FFIType } from "bun:ffi";
import { AppData } from "../data/app-data";

const libc = dlopen("libc.so.6", {
    socket: {
        args: [FFIType.i32, FFIType.i32, FFIType.i32],
        returns: FFIType.i32,
    },
    bind: {
        args: [FFIType.i32, FFIType.ptr, FFIType.i32],
        returns: FFIType.i32,
    },
    close: { args: [FFIType.i32], returns: FFIType.i32 },
});

const AF_INET = 2;
const SOCK_STREAM = 1;
const INADDR_ANY = 0;

function makeSockaddr(port: number): Buffer {
    const buf = Buffer.alloc(16);
    buf.writeUInt16LE(AF_INET, 0);
    buf.writeUInt16BE(port, 2);
    buf.writeUInt32BE(INADDR_ANY, 4);
    return buf;
}

export default async function getNextAvailablePort(
    startPort = AppData["DynamicPortStart"],
    maxPort = 65535,
): Promise<number> {
    const MAX_RETRIES = 5;
    let retries = 0;

    while (retries < MAX_RETRIES) {
        for (let port = startPort; port <= maxPort; port++) {
            const fd = libc.symbols.socket(AF_INET, SOCK_STREAM, 0);
            if (fd < 0) continue;

            const addr = makeSockaddr(port);
            const result = libc.symbols.bind(fd, ptr(addr), addr.byteLength);
            libc.symbols.close(fd);

            if (result === 0) return port;

            await Bun.sleep(2000);
        }
    }

    throw new Error(`No available port found in range ${startPort}-${maxPort}`);
}
