#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0

#define FETCH_BUF_SIZE (32 * 1024)

#include "mongoose.c"

bool mip_x_test(struct mg_mgr *);


#ifdef MQTT_HOST
// we'll generate MQTT_URL
#ifndef MQTT_HOST_NAME
#define MQTT_HOST_NAME "localhost"
#endif
#else
#define MQTT_URL "mqtt://broker.hivemq.com:1883"
#endif
#if MG_TLS == MG_TLS_BUILTIN
#define MQTTS_URL "mqtts://mongoose.ws:8883"  // test requires TLS 1.3
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
    "-----BEGIN CERTIFICATE-----\n"
    "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\n"
    "-----END CERTIFICATE-----\n";
#elif MG_TLS
#ifdef MQTT_HOST
// we'll generate MQTTS_URL
#define MQTTS_CA mg_str(s_ca_cert)
static const char *s_ca_cert =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n"
    "b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n"
    "TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n"
    "fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n"
    "epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n"
    "us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n"
    "-----END CERTIFICATE-----\n";
#else
#define MQTTS_URL "mqtts://broker.hivemq.com:8883"
#define MQTTS_CA mg_unpacked("/data/ca.pem")
#endif // MQTT_HOST
#endif

static char *host_ip, *host_ip6;

static int s_num_tests = 0;
static bool s_error = false;

#ifdef NO_ABORT
static int s_abort = 0;
#define ABORT() ++s_abort, s_error = true
#else
#ifdef NO_SLEEP_ABORT
#define ABORT() abort()
#else
#define ABORT()                       \
  sleep(2); /* 2s, GH print reason */ \
  abort();
#endif
#endif

#define ASSERT(expr)                                            \
  do {                                                          \
    s_num_tests++;                                              \
    if (!(expr)) {                                              \
      printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \
      fflush(stdout);                                           \
      ABORT();                                                  \
    }                                                           \
  } while (0)


static struct mg_http_message gethm(const char *buf) {
  struct mg_http_message hm;
  memset(&hm, 0, sizeof(hm));
  mg_http_parse(buf, strlen(buf), &hm);
  return hm;
}

static int cmpbody(const char *buf, const char *str) {
  struct mg_str s = mg_str(str);
  struct mg_http_message hm = gethm(buf);
  size_t len = strlen(buf);
  if (hm.body.len > len) hm.body.len = len - (size_t) (hm.body.buf - buf);
  return mg_strcmp(hm.body, s);
}

#ifndef NO_HTTPSERVER_TEST
static void eh1(struct mg_connection *c, int ev, void *ev_data) {
  struct mg_tls_opts *topts = (struct mg_tls_opts *) c->fn_data;
  if (ev == MG_EV_ACCEPT && topts != NULL) mg_tls_init(c, topts);
  if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    MG_DEBUG(("[%.*s %.*s] message len %d", (int) hm->method.len,
              hm->method.buf, (int) hm->uri.len, hm->uri.buf,
              (int) hm->message.len));
    if (mg_match(hm->uri, mg_str("/foo/*"), NULL)) {
      mg_http_reply(c, 200, "", "uri: %.*s", hm->uri.len - 5, hm->uri.buf + 5);
    } else if (mg_match(hm->uri, mg_str("/ws"), NULL)) {
      mg_ws_upgrade(c, hm, NULL);
    } else if (mg_match(hm->uri, mg_str("/body"), NULL)) {
      mg_http_reply(c, 200, "", "%.*s", (int) hm->body.len, hm->body.buf);
    } else {
      struct mg_http_serve_opts sopts;
      memset(&sopts, 0, sizeof(sopts));
      sopts.root_dir = "./data";
      mg_http_serve_dir(c, hm, &sopts);
    }
  } else if (ev == MG_EV_WS_OPEN) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    ASSERT(mg_strcmp(hm->uri, mg_str("/ws")) == 0);
    mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY);
  } else if (ev == MG_EV_WS_MSG) {
    struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
    mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_BINARY);
  }
}
#endif

struct fetch_data {
  char *buf;
  const char *url;
  int code, closed;
};

static void fcb(struct mg_connection *c, int ev, void *ev_data) {
  struct fetch_data *fd = (struct fetch_data *) c->fn_data;
  if (ev == MG_EV_CONNECT) {
    MG_DEBUG(("CONNECT"));
    if (mg_url_is_ssl(fd->url)) {
      struct mg_tls_opts opts;
      memset(&opts, 0, sizeof(opts));  // read CA from packed_fs
      if (host_ip != NULL && strstr(fd->url, host_ip) != NULL) {
        MG_DEBUG(("Local connection, using self-signed certificates"));
        opts.name = mg_str_s("localhost");
        opts.ca = mg_unpacked("/certs/ca.crt");
      } else {
        opts.name = mg_url_host(fd->url);
        opts.ca = mg_unpacked("/data/ca.pem");
      }
      mg_tls_init(c, &opts);
    }
  } else if (ev == MG_EV_HTTP_MSG) {
    struct mg_http_message *hm = (struct mg_http_message *) ev_data;
    snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
             hm->message.buf);
    fd->code = atoi(hm->uri.buf);
    fd->closed = 1;
    c->is_closing = 1;
    MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len,
              hm->message.buf));
    (void) c;
  } else if (ev == MG_EV_CLOSE) {
    MG_DEBUG(("CLOSE"));
    fd->closed = 1;
  } else if (ev == MG_EV_READ) {
    long bytes = *(long *) ev_data;
    MG_DEBUG(("READ %d: %.*s", (int) bytes, (int) bytes, c->recv.buf));
  }
}

static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
                 const char *fmt, ...) {
  struct fetch_data fd = {buf, url, 0, 0};
  int i;
  struct mg_connection *c = NULL;
  va_list ap;
  mg_mgr_poll(mgr, 0);  // update ifp->now to avoid ARP lookup using an old
                        // timestamp (from an ancient call in other test)
  mg_mgr_poll(mgr, 0);  // (prior idling collects lots of frames)
  c = mg_http_connect(mgr, url, fcb, &fd);
  ASSERT(c != NULL);
  va_start(ap, fmt);
  mg_vprintf(c, fmt, &ap);
  va_end(ap);
  buf[0] = '\0';
  // - TLS: multiple (small) records: allow enough loops so mg_mgr_poll can
  // process buffered records when no more frames are coming in
  for (i = 0; i < 500 && buf[0] == '\0' && !fd.closed; i++) {
    mg_mgr_poll(mgr, 0);
    usleep(5000);  // 5 ms. Slow down poll loop to ensure packet transit, but
                   // allow enough loops to get the ARP response, otherwise,
                   // given enough traffic, the timer expires before we get a
                   // chance to see the response
  }
  if (!fd.closed) c->is_closing = 1;
  mg_mgr_poll(mgr, 0);
  return fd.code;
}

static void test_http_client(struct mg_mgr *mgr) {
  char buf[FETCH_BUF_SIZE];
  int rc = 0;
  const bool ipv6 =
#ifdef IPV6_NOROUTING
      0;  // GitHub does not route IPv6 (as far as I can tell)
#else
      MG_ENABLE_IPV6;
#endif
#if MG_TLS
  if (ipv6) {
    rc = fetch(mgr, buf, "https://ipv6.google.com",
               "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
  } else {
    rc = fetch(mgr, buf, "https://cesanta.com",
               "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
  }
  ASSERT(rc == 200);  // OK
#else
  if (ipv6) {
    rc = fetch(mgr, buf, "http://ipv6.google.com",
               "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n");
    ASSERT(rc == 200);  // OK
  } else {
    rc = fetch(mgr, buf, "http://cesanta.com",
               "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n");
    ASSERT(rc == 301);  // OK: Permanently moved (HTTP->HTTPS redirect)
  }

#endif
}

static struct mg_connection *s_conn;
static char s_topic[16];

struct mqtt_data {
  char *url;
  bool passed;
};

static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data) {
  struct mqtt_data *data = (struct mqtt_data *) c->fn_data;
  if (ev == MG_EV_CONNECT) {
    MG_DEBUG(("CONNECT"));
#if MG_TLS
    struct mg_tls_opts opts;
    memset(&opts, 0, sizeof(opts));
    opts.ca = MQTTS_CA;
#if defined(MQTT_HOST) && MG_TLS != MG_TLS_BUILTIN
    opts.name = mg_str_s(MQTT_HOST_NAME);
    printf("HOST_NAME: %.*s\n", (int) opts.name.len, opts.name.buf);
#else
    opts.name = mg_url_host(data->url);
#endif
    mg_tls_init(c, &opts);
#endif
  } else if (ev == MG_EV_MQTT_OPEN) {
    MG_DEBUG(("MQTT CONNECT"));
    struct mg_mqtt_opts sub_opts;
    memset(&sub_opts, 0, sizeof(sub_opts));
    sub_opts.topic = mg_str(mg_random_str(s_topic, sizeof(s_topic)));
    sub_opts.qos = 1;
    mg_mqtt_sub(c, &sub_opts);
    struct mg_mqtt_opts pub_opts;
    memset(&pub_opts, 0, sizeof(pub_opts));
    pub_opts.topic = sub_opts.topic;
    pub_opts.message = mg_str("hi");
    pub_opts.qos = 1, pub_opts.retain = false;
    mg_mqtt_pub(c, &pub_opts);
  } else if (ev == MG_EV_MQTT_MSG) {
    struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
    MG_DEBUG(("TOPIC: %.*s, MSG: %.*s", (int) mm->topic.len, mm->topic.buf,
              mm->data.len > 10 ? 10 : (int) mm->data.len, mm->data.buf));
    ASSERT(mm->topic.len == strlen(s_topic) &&
           strncmp(mm->topic.buf, s_topic, mm->topic.len) == 0);
    if (mm->data.len == 2 && strncmp(mm->data.buf, "hi", 2) == 0) {
      struct mg_mqtt_opts pub_opts;
      memset(&pub_opts, 0, sizeof(pub_opts));
      pub_opts.topic = mm->topic;
      // send more than 1 record, content is not relevant
      pub_opts.message = mg_str_n((char *)(size_t) mqtt_fn, 21098);
      pub_opts.qos = 1, pub_opts.retain = false;
      mg_mqtt_pub(c, &pub_opts);
    } else if (mm->data.len == 8 && strncmp(mm->data.buf, "farewell", 8) == 0) {
      // close on farewell
      MG_INFO(("%lu CLOSING", c->id));
      mg_mqtt_disconnect(c, NULL);
      data->passed = true;
    } else if (mm->data.len == 21098) {
      struct mg_mqtt_opts pub_opts;
      ASSERT(memcmp((const char *) (size_t) mqtt_fn, mm->data.buf, 21098) == 0);
      // send farewell after receiving big data
      memset(&pub_opts, 0, sizeof(pub_opts));
      pub_opts.topic = mm->topic;
      pub_opts.message = mg_str("farewell");
      pub_opts.qos = 1, pub_opts.retain = false;
      mg_mqtt_pub(c, &pub_opts);
    }
  } else if (ev == MG_EV_CLOSE) {
    MG_DEBUG(("CLOSE"));
    s_conn = NULL;
  } else if (ev == MG_EV_ERROR) {
    MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data));
  }
}

static void test_mqtt_connsubpub(struct mg_mgr *mgr) {
  struct mqtt_data data;
  struct mg_mqtt_opts opts;
  memset(&opts, 0, sizeof(opts));
  opts.clean = true, opts.version = 4;
  data.passed = false;
#if defined(MQTT_HOST) && MG_TLS != MG_TLS_BUILTIN
  if (host_ip == NULL) {
    printf("\nMQTT_HOST defined but no HOST_IP provided, skipping MQTT tests\n");
    return;
  }
  printf("HOST_IP: %s\n", host_ip);
#endif
#if MG_TLS
#if MG_TLS != MG_TLS_BUILTIN
#if defined(MQTT_HOST)
  data.url = mg_mprintf("mqtts://%s:8883", host_ip);
#else
  data.url = strdup(MQTTS_URL);
#endif
#else // MG_TLS != MG_TLS_BUILTIN
#if defined(MQTT_HOST)
  printf("\nAssuming MQTT_HOST is NOT 1.3, ignoring it for MQTTS tests\n");
#endif
  data.url = strdup(MQTTS_URL);
#endif
#else // MG_TLS
#ifdef MQTT_HOST
  data.url = mg_mprintf("mqtt://%s:1883", host_ip);
#else
  data.url = strdup(MQTT_URL);
#endif
#endif // MG_TLS
  mg_mgr_poll(mgr, 0); // update interface timing (*)
  mg_mgr_poll(mgr, 0);
  s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
  ASSERT(s_conn != NULL);
  for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
    mg_mgr_poll(mgr, 0);
    usleep(5000);  // 5 ms (*) See fetch() above for reasons
  }
  ASSERT(data.passed);
  mg_mgr_poll(mgr, 0);
  free(data.url);
#if MG_ENABLE_IPV6 && defined(MQTT_HOST) && MG_TLS != MG_TLS_BUILTIN
  if (host_ip6 == NULL) {
    printf("\nMQTT_HOST defined but no HOST_IPV6 provided, skipping MQTT IPV6 tests\n");
    return;
  }
  printf("HOST_IPV6: %s\n", host_ip6);
#if MG_TLS
#if MG_TLS != MG_TLS_BUILTIN
#if defined(MQTT_HOST)
  data.url = mg_mprintf("mqtts://[%s]:8883", host_ip6);
#else
  printf("\nMQTT[S] tests over IPv6 require MQTT_HOST, skipping\n");
  return;
  //data.url = strdup(MQTTS_URL);
#endif
#else // MG_TLS != MG_TLS_BUILTIN
#if defined(MQTT_HOST)
  printf("\nAssuming MQTT_HOST is NOT 1.3, ignoring it for MQTTS tests\n");
  printf("\nMQTT[S] tests over IPv6 require MQTT_HOST, skipping\n");
  return;
#endif
  printf("\nMQTT[S] tests over IPv6 require MQTT_HOST, skipping\n");
  return;
  //data.url = strdup(MQTTS_URL);
#endif
#else // MG_TLS
#ifdef MQTT_HOST
  data.url = mg_mprintf("mqtt://[%s]:1883", host_ip6);
#else
  printf("\nMQTT[S] tests over IPv6 require MQTT_HOST, skipping\n");
  return;
  //data.url = strdup(MQTT_URL);
#endif
#endif // MG_TLS
  data.passed = false;
  mg_mgr_poll(mgr, 0); // update interface timing (*)
  mg_mgr_poll(mgr, 0);
  s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data);
  ASSERT(s_conn != NULL);
  for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) {
    mg_mgr_poll(mgr, 0);
    usleep(5000);  // 5 ms (*) See fetch() above for reasons
  }
  ASSERT(data.passed);
  mg_mgr_poll(mgr, 0);
  free(data.url);
#endif
}

#ifndef NO_HTTPSERVER_TEST
#include <pthread.h>
static void *poll_thread(void *p) {
  struct mg_mgr *mgr = (struct mg_mgr *) p;
  int i;
  for (i = 0; i < 300; i++) {
    mg_mgr_poll(mgr, 0);
    usleep(10000);  // 10 ms. Slow down poll loop to ensure packet transit
  }
  return NULL;
}
#endif

static void test_http_server(struct mg_mgr *mgr) {
#ifdef NO_HTTPSERVER_TEST
  (void) mgr;
#else
  struct mg_connection *c;
  char *cmd;
  pthread_t thread_id = (pthread_t) 0;
#if MG_TLS
  struct mg_tls_opts opts;
  memset(&opts, 0, sizeof(opts));
  mg_mem_files = mg_packed_files;  // Use generated packed filesystem
  // opts.ca = mg_str(s_tls_ca);
  opts.cert = mg_unpacked("/certs/server.crt");
  opts.key = mg_unpacked("/certs/server.key");
  mg_hexdump(opts.cert.buf, opts.cert.len);
  mg_hexdump(opts.key.buf, opts.key.len);
  c = mg_http_listen(mgr, "https://0.0.0.0:12347", eh1, &opts);
  cmd = mg_mprintf("./mip_curl.sh --insecure https://%M:12347", mg_print_ip4,
                   &mgr->ifp->ip);
#else
  c = mg_http_listen(mgr, "http://0.0.0.0:12347", eh1, NULL);
  cmd =
      mg_mprintf("./mip_curl.sh http://%M:12347", mg_print_ip4, &mgr->ifp->ip);
#endif
  ASSERT(c != NULL);
  ASSERT (mg_send(c, "NADA", 0)); // check mg_send allows len=0
  pthread_create(&thread_id, NULL, poll_thread,
                 mgr);  // simpler this way, no concurrency anyway
  MG_DEBUG(("CURL"));
  ASSERT(system(cmd) == 0);  // wait for curl
  MG_DEBUG(("MONGOOSE"));
  pthread_join(thread_id, NULL);  // wait for Mongoose
  MG_DEBUG(("DONE"));
  free(cmd);
#endif
}

static void test_tls(struct mg_mgr *mgr) {
#if MG_TLS
  char *url;
  char buf[FETCH_BUF_SIZE];  // make sure it can hold Makefile
  mg_mem_files = mg_packed_files;  // Use generated packed filesystem
  struct mg_str data = mg_unpacked("/Makefile");
  if (host_ip == NULL) {
    printf("\nNo HOST_IP provided, skipping TLS tests\n");
    return;
  }
  printf("HOST_IP: %s\n", host_ip);
  printf("NO SIMPLE TLS TEST, no known way to connect to ourselves and no server in HOST_IP\n");
#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && \
    MG_ENABLE_CHACHA20  // skip for non-CHACHA tests
  // - Fire patched server, test multiple TLS records per TCP segment handling
  // skip other TLS stacks to avoid "bad client hello", we are 1.3 only
  if (access("tls_multirec/server", X_OK) == 0) {
    url = mg_mprintf("https://%s:8443", host_ip);  
    ASSERT(system("tls_multirec/server -d tls_multirec &") == 0);
    sleep(1);
    // fetch() needs to loop enough times in order to process all TLS records;
    // otherwise it will end with 200 and shorter file contents
    ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200);
    ASSERT(cmpbody(buf, data.buf) == 0);  // "thefile" links to Makefile
    ASSERT(system("killall tls_multirec/server") == 0);
    free(url);
  } else {
    printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n");
  }
#else
  printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n");
  (void) cmpbody("", "");
  (void) mgr;
  (void) url; // these three: NO SIMPLE TLS TEST
  (void) buf;
  (void) data;
#endif
#else
  (void) cmpbody("", "");
  (void) mgr;
#endif
}

#if MG_TLS
static bool unixms(void) {
  bool res = false;
#if MG_ARCH == MG_ARCH_UNIX
  struct timeval tv = {0, 0};
  gettimeofday(&tv, 0);
  mg_boot_timestamp_ms = (uint64_t)((int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000);
  res = true;
#endif
  return res;
}

static void sntpcb(struct mg_connection *c, int ev, void *ev_data) {
  if (ev == MG_EV_SNTP_TIME) *(int64_t *) c->fn_data = *(int64_t *) ev_data;
}

static bool sntpms(struct mg_mgr *mgr, const char *url) {
  int64_t ms = 0;
  int i;
  mg_sntp_connect(mgr, url, sntpcb, &ms);
  for (i = 0; i < 50 && ms == 0; i++) {
    mg_mgr_poll(mgr, 0);
    usleep(10000);  // 10 ms. Slow down poll loop to ensure packet transit
  }
  return ms > 0;
}

static void gettimefortls(struct mg_mgr *mgr) {
  // NOTE(): historical NTP port blockage issue; expect at least one to be
  // reachable and work. https://github.com/actions/runner-images/issues/5615
  if (sntpms(mgr, "udp://time.apple.com:123") ||
      sntpms(mgr, "udp://time.windows.com:123") || sntpms(mgr, NULL) ||
      unixms()) return;
  mg_boot_timestamp_ms = (uint64_t) time(NULL) * 1000;
}
#endif

bool mip_x_test(struct mg_mgr *mgr) {

  host_ip = getenv("HOST_IP");
  host_ip6 = getenv("HOST_IPV6");

#if MG_TLS
  // Feel free to move to other module to support more systems...
  gettimefortls(mgr);
#endif

#define DASHBOARD(x)  printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true");

  usleep(500000);  // 500 ms
  s_error = false;
  test_http_client(mgr);
  DASHBOARD("http_client");

  usleep(500000);  // 500 ms
  s_error = false;
  test_http_server(mgr);
  DASHBOARD("http_server");

  usleep(500000);  // 500 ms
  s_error = false;
  test_tls(mgr);
  DASHBOARD("tls");

  usleep(500000);  // 500 ms
  s_error = false;
  test_mqtt_connsubpub(mgr);
  DASHBOARD("mqtt");

  // Clear
  s_error = false;
  mg_mgr_free(mgr);
  ASSERT(mgr->conns == NULL);  // Deconstruction OK
  printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true");
 // last entry with no comma

#ifdef NO_ABORT
  if (s_abort != 0) return false;
#endif

  printf("SUCCESS. Total tests: %d\n", s_num_tests);
  return true;
}
