#!/usr/bin/perl

use strict;
use warnings;

use IO::Async::Loop;
use Net::Async::Gearman::Worker;

use JSON;

# TODO: consider some sort of async DBI?
# That said; DBD::sqlite doesn't matter
use DBI;

my $loop = IO::Async::Loop->new;
my $worker = Net::Async::Gearman::Worker->new;

$loop->add( $worker );

my $dbh = DBI->connect( "dbi:SQLite:dbname=bunny.db", "", "" )
   or die DBI->errstr;

my $select_ent = $dbh->prepare( "SELECT eid FROM entities WHERE entity = ?" );
my $insert_ent = $dbh->prepare( "INSERT INTO entities (entity) VALUES (?)" );

my %eids;
sub eid_from_entity
{
   my ( $entity ) = @_;

   # $eid is never zero
   return $eids{$entity} ||= do {
      my $eid;
      if( $select_ent->execute( $entity ) + 0 ) {
         $eid = $select_ent->fetchrow_hashref->{eid};
         $select_ent->finish;
      }
      if( !$eid ) {
         $insert_ent->execute( $entity );
         $select_ent->execute( $entity );
         $eid = $select_ent->fetchrow_hashref->{eid};
         $select_ent->finish;
      }
      $eid;
   };
}

my $select_dp = $dbh->prepare( "SELECT dpid FROM datapoints WHERE datapoint = ?" );
my $insert_dp = $dbh->prepare( "INSERT INTO datapoints (datapoint) VALUES (?)" );

my %dpids;
sub dpid_from_datapoint
{
   my ( $datapoint ) = @_;

   # $dpid is never zero
   return $dpids{$datapoint} ||= do {
      my $dpid;
      if( $select_dp->execute( $datapoint ) + 0 ) {
         $dpid = $select_dp->fetchrow_hashref->{dpid};
         $select_dp->finish;
      }
      if( !$dpid ) {
         $insert_dp->execute( $datapoint );
         $select_dp->execute( $datapoint );
         $dpid = $select_dp->fetchrow_hashref->{dpid};
         $select_dp->finish;
      }
      $dpid;
   };
}

my $insert_pob = $dbh->prepare( "INSERT INTO pobs (timestamp, eid, dpid, value) VALUES (?, ?, ?, ?);" );

sub insert_data
{
   my ( $timestamp, $eid, $datapoint, $data ) = @_;

   if( ref $data eq "HASH" ) {
      insert_data( $timestamp, $eid, "$datapoint.$_", $data->{$_} ) for keys %$data;
   }
   else {
      my $dpid = dpid_from_datapoint $datapoint;

      $insert_pob->execute( $timestamp, $eid, $dpid, $data );
   }
}

$worker->add_function( "bunny/submit" => sub {
   my ( $job ) = @_;
   my $params = decode_json $job->arg;

   my $entity = $params->{entity};

   $dbh->{AutoCommit} = 0;
   $dbh->{RaiseError} = 1;
   eval {
      my $eid = eid_from_entity $entity;
      insert_data( $params->{timestamp} // time(), $eid, "host", $params->{values} );
      $dbh->commit;
   };
   if($@) {
      my $e = "$@";
      eval { $dbh->rollback };
      print STDERR "ERR: $e\n";
   }

   return Future->done("");
});

$worker->connect(
   host => "cel.leonerd.org.uk",
)->get;

$loop->run;
