#! /usr/bin/env perl

use strict;
use warnings;
use utf8;
use Hydra::Config;
use Hydra::Helper::Nix;
use Net::Statsd;
use File::Slurper qw(read_text);
use JSON::MaybeXS;
use Getopt::Long qw(:config gnu_getopt);
use LWP::UserAgent;

STDERR->autoflush(1);
binmode STDERR, ":encoding(utf8)";

my $config = getHydraConfig();
my $statsdConfig = getStatsdConfig($config);
$Net::Statsd::HOST = $statsdConfig->{'host'};
$Net::Statsd::PORT = $statsdConfig->{'port'};

sub gauge {
    my ($name, $val) = @_;
    die unless defined $val;
    Net::Statsd::gauge($name, $val);
}

sub sendQueueRunnerStats {
    my $endpoint = $config->{'queue_runner_endpoint'}
        or die "queue_runner_endpoint not configured in hydra.conf\n";

    my $ua = LWP::UserAgent->new();
    my $resp = $ua->get($endpoint . "/status");
    die "cannot get queue runner stats: " . $resp->status_line . "\n"
        unless $resp->is_success;

    my $json = decode_json($resp->decoded_content)
        or die "cannot decode queue runner status";

    my $qr = $json->{queueRunner};

    gauge("hydra.queue.up", $qr->{status} eq "up" ? 1 : 0);

    return if $qr->{status} ne "up";

    gauge("hydra.queue.steps.building", $qr->{nrStepsBuilding});
    gauge("hydra.queue.steps.runnable", $qr->{nrStepsRunnable});
    gauge("hydra.queue.steps.unfinished", $qr->{nrStepsUnfinished});
    gauge("hydra.queue.steps.finished", $qr->{nrStepsDone});
    gauge("hydra.queue.steps.retries", $qr->{nrRetries});
    gauge("hydra.queue.steps.unsupported", $qr->{nrUnsupportedSteps});
    gauge("hydra.queue.steps.max_retries", $qr->{maxNrRetries});
    if ($qr->{nrStepsDone}) {
        gauge("hydra.queue.steps.avg_total_time", $qr->{avgStepTimeMs} / 1000);
        gauge("hydra.queue.steps.avg_build_time", $qr->{avgStepBuildTimeMs} / 1000);
    }
    foreach my $system (keys %{$qr->{queueStats}}) {
        my $systemKey = $system =~ s/:|,/_/gr;
        gauge("hydra.queue.$systemKey.runnable", $qr->{queueStats}->{$system}->{totalRunnable});
        gauge("hydra.queue.$systemKey.active_runnable", $qr->{queueStats}->{$system}->{activeRunnable});
    }

    gauge("hydra.queue.builds.read", $qr->{nrBuildsRead});
    gauge("hydra.queue.builds.unfinished", $qr->{nrBuildsUnfinished});
    gauge("hydra.queue.builds.finished", $qr->{nrBuildsDone});

    gauge("hydra.queue.checks", $qr->{nrQueueWakeups});

    gauge("hydra.queue.machines.total", $qr->{machinesTotal});
    gauge("hydra.queue.machines.in_use", $qr->{machinesInUse});
}


sub showHelp {
    print <<EOF;
Usage: $0 [--once]

Send stats to statsd. The --once flag can be used to send one round
of stats and then exit.

Example:
  \$ $0 --once
EOF
    exit 0;
}

my $once = 0;

GetOptions("once" => \$once,
           "help" => sub { showHelp() }
    ) or exit 1;

while (1) {
    eval {
        sendQueueRunnerStats();
        1;
    } or do { warn "$@"; };

    my $meminfo = read_text("/proc/meminfo") // "";
    $meminfo =~ m/Dirty:\s*(\d+) kB/;
    if (defined $1) {
        my $dirty = $1 / (1024.0 * 1024.0);
        gauge("hydra.mem.dirty", $dirty);
    }

    if ($once) {
        last;
    }
    sleep(30);
}
