{ lib, pkgs, ... }:

# Based on https://quickwit.io/docs/log-management/send-logs/using-vector

{
  name = "vector-syslog-quickwit";
  meta.maintainers = [ pkgs.lib.maintainers.happysalada ];

  nodes = {
    quickwit =
      { config, pkgs, ... }:
      {
        environment.systemPackages = [ pkgs.jq ];

        networking.firewall.allowedTCPPorts = [ 7280 ];

        services.quickwit = {
          enable = true;
          settings = {
            listen_address = "::";
          };
        };
      };

    syslog =
      { config, pkgs, ... }:
      {
        services.vector = {
          enable = true;

          settings = {
            sources = {
              generate_syslog = {
                type = "demo_logs";
                format = "syslog";
                interval = 0.5;
              };
            };

            transforms = {
              remap_syslog = {
                inputs = [ "generate_syslog" ];
                type = "remap";
                source = ''
                  structured = parse_syslog!(.message)
                  .timestamp_nanos = to_unix_timestamp!(structured.timestamp, unit: "nanoseconds")
                  .body = structured
                  .service_name = structured.appname
                  .resource_attributes.source_type = .source_type
                  .resource_attributes.host.hostname = structured.hostname
                  .resource_attributes.service.name = structured.appname
                  .attributes.syslog.procid = structured.procid
                  .attributes.syslog.facility = structured.facility
                  .attributes.syslog.version = structured.version
                  .severity_text = if includes(["emerg", "err", "crit", "alert"], structured.severity) {
                    "ERROR"
                  } else if structured.severity == "warning" {
                    "WARN"
                  } else if structured.severity == "debug" {
                    "DEBUG"
                  } else if includes(["info", "notice"], structured.severity) {
                    "INFO"
                  } else {
                   structured.severity
                  }
                  .scope_name = structured.msgid
                  del(.message)
                  del(.host)
                  del(.timestamp)
                  del(.service)
                  del(.source_type)
                '';
              };
            };

            sinks = {
              #emit_syslog = {
              #  inputs = ["remap_syslog"];
              #  type = "console";
              #  encoding.codec = "json";
              #};
              quickwit_logs = {
                type = "http";
                method = "post";
                inputs = [ "remap_syslog" ];
                encoding.codec = "json";
                framing.method = "newline_delimited";
                uri = "http://quickwit:7280/api/v1/otel-logs-v0_7/ingest";
              };
            };
          };
        };
      };
  };

  testScript =
    let
      aggregationQuery = pkgs.writeText "aggregation-query.json" ''
        {
          "query": "*",
          "max_hits": 0,
          "aggs": {
            "count_per_minute": {
              "histogram": {
                  "field": "timestamp_nanos",
                  "interval": 60000000
              },
              "aggs": {
                "severity_text_count": {
                  "terms": {
                    "field": "severity_text"
                  }
                }
              }
            }
          }
        }
      '';
    in
    ''
      quickwit.wait_for_unit("quickwit")
      quickwit.wait_for_open_port(7280)
      quickwit.wait_for_open_port(7281)

      quickwit.wait_until_succeeds(
        "journalctl -o cat -u quickwit.service | grep 'transitioned to ready state'"
      )

      syslog.wait_for_unit("vector")
      syslog.wait_until_succeeds(
        "journalctl -o cat -u vector.service | grep 'Vector has started'"
      )

      quickwit.wait_until_succeeds(
        "journalctl -o cat -u quickwit.service | grep 'publish-new-splits'"
      )

      # Wait for logs to be generated
      # Test below aggregates by the minute
      syslog.sleep(60 * 2)

      quickwit.wait_until_succeeds(
        "curl -sSf -XGET http://127.0.0.1:7280/api/v1/otel-logs-v0_7/search?query=severity_text:ERROR |"
        + " jq '.num_hits' | grep -v '0'"
      )

      quickwit.wait_until_succeeds(
        "journalctl -o cat -u quickwit.service | grep 'SearchRequest'"
      )

      quickwit.wait_until_succeeds(
        "curl -sSf -XPOST -H 'Content-Type: application/json' http://127.0.0.1:7280/api/v1/otel-logs-v0_7/search --data @${aggregationQuery} |"
        + " jq '.num_hits' | grep -v '0'"
      )

      quickwit.wait_until_succeeds(
        "journalctl -o cat -u quickwit.service | grep 'count_per_minute'"
      )
    '';
}
