#!/usr/bin/perl

package Cog; # persistant Gearman Worker - see the pun?

use Moose;
use JSON::XS qw();
extends 'Gearman::Driver::Worker::Base';

use lib 'lib';
with 'APKPM::Gearman';
with 'APKPM::Gearman::Client';

sub prefix { '' }

package main;

use Gearman::Driver;
use Data::Dump qw(dump);

use Regexp::Common qw(net);
use Redis;

use lib 'lib';
use H1::ZTEDSLAM;
use H1::ZTEMSAN;

my $variant = $ARGV[0] || 'ZTEDSLAM';
warn "variant: $variant\n";

my $driver = Gearman::Driver->new(
	server   => 'localhost:4730',
	interval => 60,
	loglevel   => 'DEBUG',
	logfile    => "log/$variant.log",
) || die $!;

my $w1 = 'Cog';
my $worker = $w1->new();


my $crm = $worker->do( 'CRM_search' => "TIP_UREDJAJA:$variant" );


my $poll;

foreach my $user ( @$crm ) {
	next unless $user->{IP_UREDAJA} =~ /$RE{net}{IPv4}/;
	$poll->{ $user->{IP_UREDAJA} }->{ $user->{SHELF_SLOT_PORT} }++;
}

warn "# poll = ", dump($poll);

foreach my $ip ( keys %$poll ) {

	my $method = $variant . '_' . $ip;

	my $module = 'H1::' . $variant;
	my $zte = $module->new( ip => $ip );

	$driver->add_job({
		max_processes => 1, # FIXME increase?
		min_processes => 1,
		name		=> $method,
		worker		=> $worker,
		methods	   => [
			{
				decode => 'd_json',
#				encode => 'e_json',
				name   => $method,
				body   => sub {

# XXX -- worker body

		my ( $self, $job, $crm ) = @_;

		my $redis = Redis->new;

		if ( exists $crm->{logout} ) {
			$zte->logout;
			$redis->sadd("$variant.$ip.logout", $self->datetime_now);
			return { logout => 'ok' };
		}

		my $port     = $crm->{SHELF_SLOT_PORT} || die "no SHELF_SLOT_PORT";
		my $username = $crm->{USERNAME}        || die "no USERNAME";

		if ( ! $port ) {
			$redis->sadd("$variant.invalid.SHELF_SLOT_PORT" => $username );
			return "invalid port $port";
		}

		my $hash;
		eval { $hash = $zte->hash( $port ) };

		if ( $@ ) {
			$redis->sadd("$variant.$ip.error" => $@);
			return "error: $@";
		} elsif ( ! $hash ) {
			$redis->sadd("$variant.$ip.empty" => $port);
			return "empty";
		} else {
			$redis->sadd("$variant.$ip.ok" => $port);
		}

		$self->do_background_json( 'Store_insert', {
			_table => 'dslam_h',
			ip => $crm->{IP_MANAGEMENT}, # FIXME IP_UREDAJA ?
			username => $username,
			timestamp => $self->datetime_now,
			h => $self->to_hstore($hash),
		});

		return "ok $username $ip $port"; # body end

# XXX -- worker body
				},
			},
		]
	});

	warn "$method added\n";

}

$driver->add_job({
	max_processes => 1,
	min_processes => 1,
	worker		=> $worker,
	name		=> "poll_$variant",
	methods	   => [ {
		name		=> "poll_$variant",
		body   => sub {

		my ( $self, $job, $workload ) = @_;

		my $crm = $self->do( 'CRM_search' => "TIP_UREDJAJA:$variant" );

		warn "# crm $crm";

		my $gc = Gearman::Client->new;
		$gc->job_servers( $self->server || 'localhost:4730' );
		my $taskset = $gc->new_task_set;
		my $seen;

		my $redis = Redis->new;
		$redis->del( $_ ) foreach $redis->keys("$variant.*");

		$redis->set( "$variant.time.start" => $self->datetime_now );

		foreach my $user ( @$crm ) {
			my $ip   = $user->{IP_UREDAJA};
			my $port = $user->{SHELF_SLOT_PORT};

			if ( $ip !~ /$RE{net}{IPv4}/ ) {
				$redis->sadd("$variant.invalid.IP_UREDAJA" => $ip);
				next;
			} elsif ( $port !~ m{\d+(/\d+)+} ) {
				$redis->sadd("$variant.$ip.invalid.PORT" => $port);
				next;
			} elsif ( $seen->{ $ip }->{ $port }++ ) {
				$redis->sadd("$variant.$ip.duplicate" => $port);
				next;
			}
			$redis->incr("$variant.queued");
			$redis->sadd("$variant.$ip.queued" => $port);

			my $name = $variant . '_' . $ip;
			$taskset->add_task( $name, $self->e_json( $user ), {
				on_complete => sub { $redis->sadd("$variant.$ip.complete" => $port) },
				on_fail     => sub { $redis->sadd("$variant.$ip.failed" => $port) },
			} )
		}

		warn "# queue logouts";
		foreach my $ip ( keys %$seen ) {
			$taskset->add_task( $variant . '_' . $ip, $self->e_json( { logout => 1 } ) );
		}

		warn "# wait";
		$taskset->wait;

		$redis->set( "$variant.time.finish" => $self->datetime_now );
		warn "# seen ", dump($seen);

		my $ips = scalar keys %$seen;
		return "polled $ips IPs"; # body end
		}
	} ],
});

$driver->run;

