#!/usr/bin/env perl

use Mojolicious::Lite;
use Redis;

use lib '/srv/MojoX-Gearman/lib';
use MojoX::Gearman;

use lib 'lib';
use APKPM::Model;
use Redis;
use Encode;

use Data::Dump qw(dump);

plugin 'tag_helpers';
# Documentation browser under "/perldoc" (this plugin requires Perl 5.10)
plugin 'pod_renderer';

app->secret('apkpm');

get '/' => sub {
	my $self = shift;
	$self->render('index');
};

get '/ping_http' => sub {
	my $self = shift;
	my $gearman = $self->client;
	my $pong = $gearman->post( "http://localhost:4780/ping" => { Connection => 'close' } => "some data" )->res->body;
	warn "ping = $pong";
	$self->render( 'ping', pong => $pong );
};

my $gearman = MojoX::Gearman->new; #( ioloop => Mojo::IOLoop->singleton );
$gearman->server( $ENV{GEARMAN} || 'localhost:4730' );
$gearman->timeout( 15 );

get '/ping_g' => sub {
	my $self = shift;
	my $pong = $gearman->req( 'SUBMIT_JOB', 'ping', '', "some data 2" );
	warn "ping = $pong";
	$self->render( 'ping', pong => $pong );
};

sub _render_jsonp {
	my ( $self, $json ) = @_;
	#my $data = $self->render( json => $json, partial => 1 );
	my $data = $json;
	if ( my $callback = $self->param('callback') ) {
		$data = "$callback($data)";
	}
	$self->render( data => $data, format => 'js' );
}


get '/g/:call/:args' => [ args => qr/.*/ ] => sub {
	my $self = shift;

	my $call = $self->param('call');
	my $args = $self->param('args');

	if ( $call =~ m/^(LDAP|CRM)_search/ ) {
		my $k = $1 . '.*' . $args . '*';
		my $redis = Redis->new;
		if ( my @k = $redis->keys( $k ) ) {
			my $ret = join(',', map { $redis->get($_) } @k );
			return _render_jsonp( $self, "[$ret]" );
		}
	}

	my $ret = $gearman->req( 'SUBMIT_JOB', $call, '', $args );
	warn "$call = ", dump($ret), "\n";
	die "no result for $call args: $args" unless defined $ret;
	_render_jsonp( $self, $ret );
};

get '/table/:table' => sub {
	my $self = shift;

	my $table = $self->param('table');
	my $username = $self->param('username');
	my $limit = $self->param('limit') || 1;

	my $sql = "select * from $table";
	$sql .= " where username = '$username'" if $username;
	$sql .= " limit $limit";

	my $ret;
	if ( $limit == 1 ) {
		$ret = $redis->get( "table.$table.$username" );
	} else {
		$ret = $gearman->req( 'SUBMIT_JOB', 'Store_sql', '', $sql );
	}
	_render_jsonp( $self, $ret );
};

get '/_g/status' => sub {
	my $self = shift;

	my $ret = $gearman->req( 'TEXT' => 'status' );
	warn "# status:\n$ret";

	my @c = qw(function total running available);

	my $status;
	foreach my $l ( split(/\n/,$ret) ) {
		my @v = split(/\t/, $l);
		my $h;
		$h->{$c[$_]} = $v[$_] foreach 0 .. $#v;
		push @$status, $h;
	}
	warn "## ", dump $status;
	_render_jsonp( $self, Mojo::JSON->new->encode({ status => $status }) );
};

sub _gearman_redis {
	my ( $job, $params, $timeout ) = @_;

	my $key = $params;
	$key =~ s/\W+/_/gs;
	$key =~ s/\s+/_/gs;
	$key = "sql.$key";
	warn "# _gearman_redis $key [$timeout s]";

	my $redis = Redis->new;
	my $ret;

	if ( $ret = $redis->get($key) ) {
		warn "redis hit $key\n";
	} else {
		$gearman->timeout($timeout);
		$ret = $gearman->req( 'SUBMIT_JOB', $job, '', $params );
		die "no result for $params" unless $ret;

		$redis->set($key => $ret);
	}

	$redis->expire($key => 60); # refresh redis key timeout

	$ret = Mojo::JSON->new->decode( $ret );

	if ( ! exists $ret->{error} && ref $ret->{columns} ne 'ARRAY' ) {
		$ret->{error} = 'query run longer than ' . $gearman->timeout . ' s timeout';
	}

	return $ret;
}

get '/gnuplot' => sub {
	my $self = shift;

	my $sql = $self->param('sql');

	if ( my $timeout = $self->param('timeout') ) {
		warn "set timout to $timeout";
		$gearman->timeout( $timeout );
	} else {
		$self->param( timeout => $gearman->timeout );
	}

	return $self->render('gnuplot', img => '', gnuplot => '') unless $sql;

	# re-format SQL
	$sql =~ s/\s+(from|where|order|limit|join)/\n$1/gs;
	$self->param( sql => $sql );

	my $ret = _gearman_redis( 'Store_sql' => $sql, $self->param('timeout') );

	return $self->render('gnuplot', sql => $sql, img => '', gnuplot => $ret->{error} )
		if exists $ret->{error};

	my $dir = $self->app->home->rel_dir('public');

	my $name = $sql;
	$name =~ s/\W+//gs;
	$name .= '.png';

warn "# $sql -> $name";

	mkdir "$dir/gnuplot" unless -e "$dir/gnuplot";
	my $png = "$dir/gnuplot/$name"; # FIXME

	my @c = @{ $ret->{columns} };

	my $gnuplot = qq|
set terminal png
set output '$png'
|;

	my $del; # delimiter between date and time

	if ( $ret->{rows}->[0]->[0] =~ m/(\d{4}-\d{2}-\d{2})(.)?(\d{2}:\d{2}:\d{2})?/ ) {

		my ( $date,$time );
		( $date, $del, $time ) = ( $1,$2,$3 );

		my $fmt = '%Y-%m-%d';
		$fmt   .= 'T' if $del;
		$fmt   .= '%H:%M:%S' if $time;

		my $format_x = $time ? '%d %H:%M' : '%Y-%m-%d';
		$gnuplot .= qq|

set xdata time
set timefmt "$fmt"

set format x "$format_x"
set xtics nomirror rotate by -90

|;

	} else { 
		warn "first column not timestamp";
	}

	my $plot = 'plot ';
	my $with = $self->param('with') || 'dots';
	foreach my $i ( 1 .. $#{ $ret->{rows}->[0] } ) {
		$gnuplot .= $plot; $plot = ',';
		$gnuplot .= qq| '-' using 1:2 with $with title "$c[$i]" |;
	}
	$gnuplot .= "\n";

	foreach my $i ( 1 .. $#{ $ret->{rows}->[0] } ) {

		foreach my $row ( @{ $ret->{rows} } ) {
			my $date = $row->[0];
			$date =~ s/\Q$del\E/T/g if $del;
			$date =~ s/\.\d+//;
			$gnuplot .= join(" ", $date, $row->[$i])."\n";
		}
		$gnuplot .= "e\n";
	}

	open(my $fd, '|-', 'gnuplot') || die "gnuplot: $!";
	print $fd $gnuplot;
	close($fd);

	$gnuplot = '' unless $self->param('include_gnuplot');

	$self->render('gnuplot', sql => $sql, img => "/gnuplot/$name", gnuplot => $gnuplot);
};

get '/_redis' => sub {
	my $self = shift;

	_render_jsonp( $self, Mojo::JSON->new->encode({ status => APKPM::Model->redis_status }) );
};

get '/user' => sub {
	my $self = shift;

	$self->render('user');
};

get '/psql/:command' => sub {
	my $self = shift;
	my $command = $self->param('command');
	$command =~ s/^_/\\/ && warn "internal psql command $command";
	$command = sprintf "psql -c '%s' apkpm", $command;
	my $output = `$command`; # FIXME unsecure
	$self->render_text( "\$ <b><tt>$command</tt></b>\n\n<pre>$output</pre>" );
};

# create pid file
open(my $pid, '>', '/tmp/apkpm.web_ui.pid');
print $pid "$$\n";
close $pid;

app->start;
