use v5.14;
use strict;
use warnings;

use Gruntmaster::Daemon;

use Cwd qw/getcwd/;
use File::Basename qw/fileparse/;
use File::Slurp qw/read_file/;
use File::Temp qw/tempdir/;
use File::Which qw/which/;
use FindBin;
use Hash::Merge qw/merge/;
use List::Util qw/sum/;
use Log::Log4perl;
use Test::More;
use YAML::Tiny qw/LoadFile/;

##################################################

use constant COMPILER => {
	qw/BRAINFUCK bfc
	   C gcc
	   CPP g++
	   D dmd
	   GOLFSCRIPT golfscript
	   MONO gmcs
	   JAVA javac
	   JAVASCRIPT node
	   JULIA julia
	   PASCAL fpc
	   GOLANG go
	   GCCGO gccgo
	   HASKELL ghc
	   PERL perl
	   PHP php
	   PYTHON python2.7
	   RUBY ruby
	   RUST rustc
	   OBERON obc
	   OCAML ocaml
	   SBCL sbcl/};

my %needs_fork = map { $_ => 1 } qw/GOLANG GOLFSCRIPT GCCGO JAVA JAVASCRIPT JULIA RUBY SBCL/;

my $loglevel = $ENV{TEST_LOG_LEVEL} // ($ENV{TEST_VERBOSE} ? 'TRACE' : 'OFF');
my $log_conf = <<CONF;
log4perl.category.Gruntmaster.Daemon = $loglevel, stderr

log4perl.appender.stderr                          = Log::Log4perl::Appender::Screen
log4perl.appender.stderr.layout                   = Log::Log4perl::Layout::PatternLayout
log4perl.appender.stderr.layout.ConversionPattern = [\%d] [\%F{1}:\%M{1}:\%L] [\%p] \%m\%n
CONF
Log::Log4perl->init(\$log_conf);

$ENV{PATH} = getcwd . ':' . $ENV{PATH};
$ENV{PATH} = "$FindBin::Bin/../blib/script:" . $ENV{PATH};
umask 0022;

sub check_job{
	my $meta = shift;
	if (defined $meta->{results}) {
		delete $meta->{results}[$_]{time} for keys @{$meta->{results}};
	}
	my $desc = $meta->{name} . ': ' . $meta->{test_name};
	is $meta->{result}, $meta->{expected_result}, "Result is correct ($desc)";
	is $meta->{result_text}, $meta->{expected_result_text}, "Result text is correct ($desc)";
	is_deeply $meta->{results}, $meta->{expected_results}, "Results are correct ($desc)";
}

my @problems = exists $ENV{TEST_PROBLEMS} ? map {"t/problems/$_"} split ' ', $ENV{TEST_PROBLEMS} : <t/problems/*>;
@problems = grep { $_ !~ /hello/ } @problems unless $ENV{RELEASE_TESTING} || $ENV{TEST_PROBLEMS};

plan tests => 3 * sum map { my @temp = <$_/tests/*>; scalar @temp } @problems;
note "Problems to be tested: " . join ', ', @problems;

my $tempdir = tempdir "gruntmasterd-testingXXXX", TMPDIR => 1, CLEANUP => 1;
chmod 0777, $tempdir;

my $job = 0;

for my $problem (@problems) {
	my $pbmeta = LoadFile "$problem/meta.yml";
	for (1 .. $pbmeta->{testcnt}) {
		$pbmeta->{infile}[$_ - 1] = read_file "$problem/$_.in" if $pbmeta->{generator} eq 'File';
		$pbmeta->{okfile}[$_ - 1] = read_file "$problem/$_.ok" if $pbmeta->{runner} && $pbmeta->{runner} eq 'File';
	}
	if (exists $pbmeta->{files}) {
		$_->{content} = read_file "$problem/$_->{name}" for values %{$pbmeta->{files}}
	}

  TODO: {
		note "Now testing problem $pbmeta->{name} ($pbmeta->{description})";

		for my $source (<$problem/tests/*>) {
		  SKIP: {
				my $meta = LoadFile "$source/meta.yml";
				if ($meta->{files}{prog}) {
					my $format = $meta->{files}{prog}{format};
					my $compiler = COMPILER->{$format};
					skip "$compiler not found in path", 3 unless $ENV{GRUNTMASTER_VM} || which $compiler;
					skip "$format requires multiple processes. Set GRUNTMASTER_KILL_USER and GRUNTMASTER_SUDO to allow multiple processes.", 3 if !$ENV{GRUNTMASTER_KILL_USER} && $needs_fork{$format};
					$meta->{files}{prog}{content} = read_file "$source/$meta->{files}{prog}{name}";
				}
				$meta = merge $meta, $pbmeta;
				local $TODO = $meta->{todo} if exists $meta->{todo};
				note "Running $meta->{test_name} ($meta->{test_description})...";
				my $savedcwd = getcwd;
				chdir $tempdir;
				Gruntmaster::Daemon::process $meta;
				chdir $savedcwd;
				check_job $meta;
			}
		}
	}
}
