package App::Web::NAOdash;

use 5.014000;
use strict;
use warnings;
use re '/saa';
use parent qw/Plack::Component/;

our $VERSION = '0.002';

use Digest::SHA qw/sha256_base64/;
use File::Slurp;
use HTML::TreeBuilder;
use JSON::MaybeXS qw/encode_json/;
use NetHack::NAOdash qw/naodash_user/;

my ($dash, $css, $css_hash);

{
	$css = read_file 'web/dash.css';
	$css =~ y/\n\t//d;
	$css =~ s/([):]) /$1/g;
	$css =~ s/ ([{(])/$1/g;
	$css_hash = 'sha256-' . sha256_base64($css) . '=';
	my $tb = HTML::TreeBuilder->new;
	$tb->ignore_unknown(0);
	$dash = $tb->parse_file('web/dash.html');
	$dash->find('link')->postinsert([style => $css])->detach;
}

sub format_time {
	my ($time) = @_;
	return unless defined $time;
	my %units = (
		s => 60,
		m => 60,
		h => 24,
		d => 7,
		w => 1e9,
	);
	my @parts;
	for (qw/s m h d w/) {
		use integer;
		last unless $time;
		unshift @parts, ($time % $units{$_}) . $_;
		$time /= $units{$_};
	}
	join ' ', @parts;
}

sub make_html {
	my ($name, $result) = @_;
	my @checks = @{$result->{checks}};
	my %numbers = %{$result->{numbers}};
	$numbers{totalrealtime} = format_time $numbers{totalrealtime};
	$numbers{minrealtime} = format_time $numbers{minrealtime};

	my $tree = $dash->clone;
	$tree->find('title')->delete_content->push_content("Dashboard for $name");
	$tree->find('a')->attr(href => $tree->find('a')->attr('href') . $name);
	$tree->find('a')->delete_content->push_content($name);
	for (@checks) {
		my $el = $tree->look_down(id => $_);
		warn "No element for check $_" unless $el; ## no critic (RequireCarping)
		$el->attr(class => 'done') if $el;
	}
	while (my ($id, $num) = each %numbers) {
		next unless defined $num;
		my $el = $tree->look_down(id => $id);
		warn "No element for check $id" unless $el; ## no critic (RequireCarping)
		$el->delete_content->push_content($num);
	}
	$tree->as_HTML;
}

sub reply {
	my ($code, $message, $type) = @_;
	$type //= 'text/plain';
	[$code, [
		'Cache-Control' => ($code < 500 ? 'max-age=86400' : 'no-cache'),
		'Content-Type' => "$type; charset=utf-8",
		'Content-Length' => length $message,
# Safari implements CSP Level 1 but not CSP Level 2
#		'Content-Security-Policy' => "default-src 'none'; style-src '$css_hash';",
	], [$message]]
}

sub call {
	my ($self, $env) = @_;
	return reply 400, 'Bad request: user contains characters outside [a-zA-Z0-9_]' unless $env->{PATH_INFO} =~ m{^/(\w+)$};
	my $name = $1;
	my $result = eval { naodash_user $name } or return reply 500, $@;

	return reply 200, encode_json($result), 'application/json' if $self->{json};
	return reply 200, make_html($name, $result), 'text/html';
}

1;
__END__

=encoding utf-8

=head1 NAME

App::Web::NAOdash - Analyze NetHack xlogfiles and extract statistics (web interface)

=head1 SYNOPSIS

  # In app.psgi
  use App::Web::NAOdash;
  use Plack::Builder;

  builder {
    mount '/dash/' => App::Web::NAOdash->new->to_app;
    mount '/json/' => App::Web::NAOdash->new(json => 1)->to_app;
    ...
  }

=head1 DESCRIPTION

App::Web::NAOdash is a web interface to L<NetHack::NAOdash>.

It handles URLs of the form C</username>, where I<username> is a NAO
username. It retrieves the xlogfile from NAO and returns the result of
the analysis.

The constructor takes a single named parameter, I<json>, that is false
by default. The result will be returned as HTML is I<json> is false,
as JSON if I<json> is true.

=head1 SEE ALSO

L<NetHack::NAOdash>, L<App::NAOdash>

=head1 AUTHOR

Marius Gavrilescu, E<lt>marius@ieval.roE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2015 by Marius Gavrilescu

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.20.2 or,
at your option, any later version of Perl 5 you may have available.


=cut
