// Wire round-trip test for nucleic-linux-agent, over vsock loopback (VMADDR_CID_LOCAL).
// Exercises ping + a streaming exec (stdout, stderr, exit code, stdin) exactly as the host would.
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/vm_sockets.h>

static int fails = 0;
#define CHECK(cond, msg) do { if (!(cond)) { printf("FAIL: %s\n", msg); fails++; } else { printf("ok: %s\n", msg); } } while (0)

static int connect_agent(unsigned port) {
    int fd = socket(AF_VSOCK, SOCK_STREAM, 0);
    if (fd < 0) { perror("socket"); return -1; }
    struct sockaddr_vm addr; memset(&addr, 0, sizeof addr);
    addr.svm_family = AF_VSOCK;
    addr.svm_cid = VMADDR_CID_LOCAL;  // 1 — loopback
    addr.svm_port = port;
    if (connect(fd, (struct sockaddr *)&addr, sizeof addr) != 0) { close(fd); return -1; }
    return fd;
}

static char *read_line(int fd) {
    static char buf[1 << 20];
    size_t n = 0;
    while (n < sizeof buf - 1) {
        char c;
        ssize_t r = read(fd, &c, 1);
        if (r <= 0) return NULL;
        if (c == '\n') break;
        buf[n++] = c;
    }
    buf[n] = 0;
    return buf;
}

// crude base64 decode for the test (assumes no embedded newlines)
static int b64v(char c){ if(c>='A'&&c<='Z')return c-'A'; if(c>='a'&&c<='z')return c-'a'+26; if(c>='0'&&c<='9')return c-'0'+52; if(c=='+')return 62; if(c=='/')return 63; return -1;}
static int b64dec(const char*in,char*out){int q[4],qi=0,o=0;for(const char*p=in;*p;p++){if(*p=='='){q[qi++]=0;}else{int v=b64v(*p);if(v<0)continue;q[qi++]=v;}if(qi==4){out[o++]=(q[0]<<2)|(q[1]>>4);out[o++]=(q[1]<<4)|(q[2]>>2);out[o++]=(q[2]<<6)|q[3];qi=0;}}size_t len=strlen(in),pad=0;for(size_t i=len;i>0&&in[i-1]=='=';i--)pad++;o-=pad;out[o]=0;return o;}

// extract a string value for a top-level key (simple, test-only)
static int getstr(const char*obj,const char*key,char*out){char pat[64];snprintf(pat,sizeof pat,"\"%s\":\"",key);const char*p=strstr(obj,pat);if(!p)return -1;p+=strlen(pat);int o=0;while(*p&&*p!='"'){if(*p=='\\'){p++;}out[o++]=*p++;}out[o]=0;return 0;}

int main(void) {
    unsigned port = 2035;
    const char *ep = getenv("NUCLEIC_AGENT_PORT");
    if (ep) port = (unsigned)atoi(ep);

    int fd = connect_agent(port);
    if (fd < 0) { printf("SKIP: cannot connect to vsock loopback (agent/loopback unavailable): %s\n", strerror(errno)); return 2; }

    // ping
    const char *ping = "{\"op\":\"ping\"}\n";
    write(fd, ping, strlen(ping));
    char *reply = read_line(fd);
    CHECK(reply && strstr(reply, "\"ok\":true"), "ping ok:true");
    CHECK(reply && strstr(reply, "\"version\":2"), "ping version 2");
    CHECK(reply && strstr(reply, "\"os\":\"linux\""), "ping os linux");
    close(fd);

    // exec: stdout + stderr + nonzero exit
    fd = connect_agent(port);
    const char *ex = "{\"op\":\"exec\",\"command\":\"printf hello; printf oops 1>&2; exit 7\"}\n";
    write(fd, ex, strlen(ex));
    char sout[4096] = "", serr[4096] = "";
    int exit_code = -999;
    for (;;) {
        char *l = read_line(fd);
        if (!l) break;
        char tag[8] = "";
        getstr(l, "t", tag);
        if (strcmp(tag, "o") == 0) { char d[4096]; getstr(l, "d", d); char dec[4096]; b64dec(d, dec); strcat(sout, dec); }
        else if (strcmp(tag, "e") == 0) { char d[4096]; getstr(l, "d", d); char dec[4096]; b64dec(d, dec); strcat(serr, dec); }
        else if (strcmp(tag, "x") == 0) { const char *c = strstr(l, "\"code\":"); if (c) exit_code = atoi(c + 7); break; }
    }
    close(fd);
    CHECK(strcmp(sout, "hello") == 0, "exec stdout == 'hello'");
    CHECK(strcmp(serr, "oops") == 0, "exec stderr == 'oops'");
    CHECK(exit_code == 7, "exec exit code == 7");

    // exec: stdin round-trip (cat echoes what we send, then EOF)
    fd = connect_agent(port);
    const char *cat = "{\"op\":\"exec\",\"command\":\"cat\"}\n";
    write(fd, cat, strlen(cat));
    // stdin frame: base64("ping\n") = cGluZwo=
    const char *inframe = "{\"t\":\"i\",\"d\":\"cGluZwo=\"}\n";
    write(fd, inframe, strlen(inframe));
    const char *eof = "{\"t\":\"eof\"}\n";
    write(fd, eof, strlen(eof));
    char cout[4096] = "";
    for (;;) {
        char *l = read_line(fd);
        if (!l) break;
        char tag[8] = "";
        getstr(l, "t", tag);
        if (strcmp(tag, "o") == 0) { char d[4096]; getstr(l, "d", d); char dec[4096]; b64dec(d, dec); strcat(cout, dec); }
        else if (strcmp(tag, "x") == 0) break;
    }
    close(fd);
    CHECK(strcmp(cout, "ping\n") == 0, "exec stdin→stdout round-trip");

    printf(fails ? "\n%d FAILURES\n" : "\nALL PASS\n", fails);
    return fails ? 1 : 0;
}
