#!perl
package App::test::travis;
use 5.10.0;
use strict;
use warnings;
use encoding::warnings 'FATAL';
use Fatal qw(open close);

use File::Temp qw(tempdir);
use Config qw(%Config);
use File::Spec ();
use Getopt::Long qw(:config posix_default no_ignore_case bundling auto_version);
use autouse 'Pod::Usage' => qw(pod2usage);
use autouse 'Pod::Find'  => qw(pod_where);
use YAML ();

use version; our $VERSION = version->declare('v0.9.1');

my $DRY_RUN;
my $HELP;

GetOptions(
    '--dry-run' => \$DRY_RUN,
    '--help'    => \$HELP,
) or help(1);
help(0) if $HELP;

my $path_sep = $Config{path_sep};

my %tab = (
    c => {
        script => './configure && make && make test',
    },
    cpp => {
        script => './configure && make && make test',
    },
    closure => {
        script => 'lein test',
    },
    elang => {
        install => 'rebar get-deps',
        script => 'rebar compile && rebar skip_deps=true eunit',
    },
    go => {
        install => 'go get -d -v ./... && go build -v ./...',
        script => 'make',
    },
    groovy => {
        install => 'gradle assemble',
        script => 'gradle check',
    },
    haskell => {
        install => 'cabal install --only-dependencies --enable-tests',
        script => 'cabal configure --enable-tests && cabal build && cabal test',
    },
    java => {
        install => 'mvn install -DskipTests=true',
        script => 'mvn test',
    },

    node_js => {
        setup => sub {
            my($config, $tempdir, $cb) = @_;
            local $ENV{PATH} = join $path_sep, "node_modules/.bin", $ENV{PATH};
            $cb->();
        },
        install => 'npm install',
        script => 'npm test',
    },

    perl => {
        setup => sub {
            my($config, $tempdir, $cb) = @_;
            local $ENV{PERL_CPANM_OPT} = "-l$tempdir --verbose";
            local $ENV{PERL5OPT}       = "-Mlib=$tempdir/lib/perl5";
            local $ENV{PATH}           = join $path_sep, "$tempdir/bin", $ENV{PATH};

            $cb->();
        },
        install => 'cpanm --installdeps --notest .',
        script => 'cpanm --test-only .',
    },

    php => {
        script => 'phpunit',
    },

    python => {
        install => 'pip install -r requirements.txt --use-mirrors',
    },

    ruby => {
        install => 'bundle exec',
        script => 'bundle exec rake',
    },

    scala => {
        script => 'sbt test',
    },
);

main(@ARGV);

sub main {
    my($travis_yml) = @_;

    my $start = time();

    $travis_yml //= '.travis.yml';

    my $config = YAML::LoadFile($travis_yml);

    my $language = lc($config->{language} // 'ruby');

    my $behavior = $tab{$language} or die "no behavior defined for $language\n";

    close STDIN;
    open STDIN, '<', File::Spec->devnull;

    if ($ENV{TRAVIS} && $ENV{CI}) {
        say '# skip because TRAVIS and CI are already set';
        return;
    }
    say "# running $travis_yml";

    # Travis CI Environment Variables
    # http://about.travis-ci.org/docs/user/osx-ci-environment/
    local $ENV{CI} = 'true';
    local $ENV{TRAVIS} = 'true';

    my $tempdir = tempdir('.travis-run-XXXXXXX', CLEANUP => 1);
    my $setup = $behavior->{setup} //  sub {
        my($config, $tempdir, $cb) = @_;
        $cb->();
    };

    for my $mode(qw(before_install install script)) {
        $config->{$mode} //= $behavior->{$mode};
    }

    $behavior->{setup}->($config, $tempdir, sub {
        my $versions = $config->{$language} // []; # TODO

        run_commands($config, 'before_install');
        run_commands($config, 'install');
        run_commands($config, 'before_script');
        run_commands($config, 'script');

        say '# finished: ', scalar localtime;

        my $duration = (time() - $start);
        if ($duration > 60) {
            say sprintf '# duration: %d min %d sec', int($duration / 60), $duration % 60;
        }
        else {
            say sprintf '# duration: %d sec', $duration;
        }
    });
}

sub run_commands {
    my($config, $mode) = @_;
    return unless defined $config->{$mode};
    say "# $mode";
    my @cmds = ref($config->{$mode}) eq 'ARRAY'
        ? @{$config->{$mode}}
        :  ($config->{$mode});
    for my $cmd(@cmds) {
        xsystem($cmd);
    }
}

sub xsystem {
    my(@command) = @_;

    say "\$ @command";
    unless($DRY_RUN) {
        system(@command) == 0 or die "failed to call `@command`";
    }
}

sub help {
    my($exit_status) = @_;
    my $pod_file = pod_where({ -inc => 1 }, __PACKAGE__) or die;
    pod2usage(
        -exitval => $exit_status,
        -input   => $pod_file,
    );
}
