/*
 * acq_netdrv.c - 외부 카드망 전문 왕복 드라이버 (매입사 → 밴사/발급망).
 *   실제 매입건을 골라 ISO 8583 0200 승인요청을 조립 → 외부망(acq_extsw)으로 소켓
 *   전송 → 0210 응답 수신·파싱 → 왕복시간(RTT)·응답코드와 함께 mg_netlog 적재.
 *   사용법: acq_netdrv <count> [extsw_host] [extsw_port]
 *   실제 전문 왕복이 일어남을 증명(코덱·프레이밍·소켓·상태처리 = 마이그레이션 난제).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <libpq-fe.h>
#include "acq_iso8583.h"

static long now_ms(void) {
    struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

/* extsw 로 프레임 왕복. 반환 0=성공, -1=오류 */
static int roundtrip(const char *host, int port, const char *reqwire, char *rspwire, int cap) {
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr; memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET; addr.sin_port = htons(port);
    struct hostent *he = gethostbyname(host);
    if (!he) { close(fd); return -1; }
    memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(fd); return -1; }

    char frame[4200]; int fl = snprintf(frame, sizeof(frame), "%04X%s", (int)strlen(reqwire), reqwire);
    if (write(fd, frame, fl) != fl) { close(fd); return -1; }

    char lenhex[5]; int got = 0;
    while (got < 4) { int r = read(fd, lenhex + got, 4 - got); if (r <= 0) { close(fd); return -1; } got += r; }
    lenhex[4] = 0; int rlen = (int)strtol(lenhex, NULL, 16);
    if (rlen <= 0 || rlen >= cap) { close(fd); return -1; }
    got = 0;
    while (got < rlen) { int r = read(fd, rspwire + got, rlen - got); if (r <= 0) { close(fd); return -1; } got += r; }
    rspwire[rlen] = 0;
    close(fd);
    return 0;
}

int main(int argc, char **argv) {
    int count = (argc > 1) ? atoi(argv[1]) : 10;
    const char *host = (argc > 2) ? argv[2] : "localhost";
    int port = (argc > 3) ? atoi(argv[3]) : 9500;

    const char *ci = getenv("PGCONN");
    PGconn *c = PQconnectdb(ci ? ci : "host=db user=acq password=acq dbname=acq");
    if (PQstatus(c) != CONNECTION_OK) { fprintf(stderr, "PG 연결실패: %s\n", PQerrorMessage(c)); return 1; }

    PGresult *r = PQexec(c,
        "SELECT purchase_id, amount, merchant_id, to_char(created_at,'MMDDHH24MISS'), "
        "       channel FROM purchase WHERE purchase_id < 10000000 ORDER BY random() LIMIT 200");
    int nrows = PQntuples(r);
    if (nrows == 0) { fprintf(stderr, "매입건 없음\n"); return 1; }

    int ok = 0, decl = 0, err = 0; long tot_rtt = 0;
    for (int i = 0; i < count; i++) {
        int ri = i % nrows;
        long pid = atol(PQgetvalue(r, ri, 0));
        long amt = atol(PQgetvalue(r, ri, 1));
        const char *mid = PQgetvalue(r, ri, 2);
        const char *dt = PQgetvalue(r, ri, 3);   /* MMDDhhmmss */
        char stan[7]; snprintf(stan, sizeof(stan), "%06ld", (pid + i) % 1000000);

        iso_msg_t req; iso_init(&req); strcpy(req.mti, "0200");
        iso_set(&req, 2, "4000123412341234");
        iso_set(&req, 3, "000000");
        char amtbuf[16]; snprintf(amtbuf, sizeof(amtbuf), "%ld", amt); iso_set(&req, 4, amtbuf);
        iso_set(&req, 7, dt);
        iso_set(&req, 11, stan);
        iso_set(&req, 41, "TERM0001");
        iso_set(&req, 42, mid);
        iso_set(&req, 49, "410");

        char reqwire[4096], rspwire[4096];
        int rc = iso_build(&req, reqwire, sizeof(reqwire));
        if (rc < 0) { err++; continue; }

        long t0 = now_ms();
        int rt = roundtrip(host, port, reqwire, rspwire, sizeof(rspwire));
        long rtt = now_ms() - t0;
        tot_rtt += rtt;

        const char *status = "OK", *rcode = "", *appr = "", *rmti = "", *rrn = "";
        iso_msg_t rsp;
        if (rt < 0) { status = "TIMEOUT"; err++; }
        else if (iso_parse(rspwire, &rsp) < 0) { status = "ERROR"; err++; }
        else {
            rmti = rsp.mti; rcode = iso_get(&rsp, 39); appr = iso_get(&rsp, 38); rrn = iso_get(&rsp, 37);
            if (strcmp(rcode, "00") == 0) ok++; else decl++;
        }

        char q[9000];
        snprintf(q, sizeof(q),
          "INSERT INTO mg_netlog(net_id,purchase_id,channel,peer,req_mti,rsp_mti,stan,rrn,"
          "resp_code,approval_no,amount,rtt_ms,req_wire,rsp_wire,status) VALUES "
          "(nextval('mg_netlog_seq'),%ld,'CH01','BC-NET','0200','%s','%s','%s','%s','%s',%ld,%ld,'%s','%s','%s')",
          pid, rmti, stan, rrn, rcode, appr, amt, rtt, reqwire,
          (rt < 0 ? "" : rspwire), status);
        PGresult *ir = PQexec(c, q);
        PQclear(ir);
    }
    PQclear(r);
    printf(">>> NETDRV 완료: 요청 %d · 승인 %d · 거절 %d · 오류 %d · 평균RTT %ldms\n",
           count, ok, decl, err, count ? tot_rtt / count : 0);
    PQfinish(c);
    return 0;
}
