#!/usr/bin/perl

=begin comment

Это pre-commit hook. Этот скрипт запускается перед тем как как открывается
$EDITOR для ввода commit message.

Если этот скрипт завершвется с exit status != 0, то комит не происходит.

Можно запустить `arc commit --no-verify` и тогда этот хук не будет выполнятся.

Вообще, все что тут написано — это неправильно. Все проверки, которые тут
выполняются работают с файлами на диске, а не с тем что будет закомичено.
В идельном мире нужно реализовать решение, как описано в блоге [yipit][yipit].
Но большинство комитов у нас — это комиты именно всего, поэтому сделано такое
неправильное решение, которое будет работать в большинстве случаев.

 [yipit]: http://tech.yipit.com/2011/11/16/183772396/

=end comment

=cut

use strict;
use warnings;
use v5.10;
use String::ShellQuote;
use Term::ANSIColor qw(colored);

sub say {
    print @_, "\n";
}

sub exit_with_message {
    my ($message) = @_;

    say colored(
        "There was an error in the code. Plese use `arc commit --no-verify`\n"
        . "if you need to commit this broken code. But the better approach\n"
        . "is to fix the problem.",
        "red",
    );

    if (defined $message) {
        say '';
        say $message;
    }
    exit 123;
}

sub check_server_side_whitespaces {
    my $exit_status = system("prove --norc -It_lib -It/lib -Ilib t/code_style/whitespaces.t t/code_style/pretty_json.t &> /dev/null");

    if ($exit_status != 0) {
        exit_with_message(
            "There was a problem with whitespace charachers in server side\n"
            . "code. Run `prove t/code_style/` to find the problems.\n"
            . "There is a script `whiter` that can fix that errors (run it\n"
            . "as `whiter --help` to find out how to use it)."
        );
    }
}

sub check_client_side_whitespaces {
    my $exit_status = system("prove --norc -It_lib t/code_style/whitespaces_in_blocks.t &> /dev/null");

    if ($exit_status != 0) {
        exit_with_message(
            "There was a problem with whitespace charachers in client side\n"
            . "code. Run `prove t/code_style/` to find the\n"
            . "problems. There is a script `whiter` that can fix that\n"
            . "errors (run it as `whiter --help` to find out how to use it)."
        );
    }
}

sub check_javascript {
    my @files = find_files_in_staging_area();

    my @files_with_jshint_errors;
    my @files_with_jscs_errors;

    foreach my $file (@files) {
        next if $file =~ /\.deps\.js$/;
        next if $file !~ /\.js$/;

        my $string = shell_quote($file);

        my $jshint_exit_status = system("jshint $string &> /dev/null");
        push @files_with_jshint_errors, $string if $jshint_exit_status != 0;

        my $jscs_exit_status = system("jscs $string &> /dev/null");
        push @files_with_jscs_errors, $string if $jscs_exit_status != 0;
    }


    if (@files_with_jshint_errors or @files_with_jscs_errors) {

        my $message =
            "There was errors in checking javascript files that are going\n"
            . "to be commited. See details with:\n"
            ;

        if (@files_with_jshint_errors) {
            $message .=
                "\n"
                . " "x4 . "jshint " . join(" ", @files_with_jshint_errors)
                . "\n"
                ;
        }

        if (@files_with_jscs_errors) {
            $message .=
                "\n"
                . " "x4 . "jscs " . join(" ", @files_with_jscs_errors)
                . "\n"
                ;
        }

        exit_with_message( $message );

    }
}

sub make_perltidy_and_whiter {
    my $exit_status = 0;
    my @files = find_files_in_staging_area();
    my @files_not_staged = split /\n/, `arc diff --name-only`;
    my %staged = map {$_ => 1} @files;
    my %not_staged = map {$_ => 1}  @files_not_staged;
    if (my @modified_files = grep {$not_staged{$_}} @files) {
        my $message_files = join ', ', @modified_files;
        exit_with_message(
            "Files $message_files is in 'staging area' and is in 'not staged' at the same time\n"
        );
    }


    @files = grep {/(\.t|\.pm|\.pl)\z/} @files;
    for my $f (@files) {
        `whiter $f`;
        `perltidy $f -w --backup-file-extension=.~tidybkp~`;
    }
    $exit_status = grep {$staged{$_}} (split /\n/, `arc diff --name-only`);

    `find ./ -name '*.~tidybkp~' -delete -or -name '*.ERR' -delete`;

    if ($exit_status != 0) {
        exit_with_message(
            "Perltidy or whiter changed some commit files\n"
            . "For commit do arc add + arc commit\n"
        );
    }
}

sub find_files_in_staging_area {

    my $output = `arc diff --cached HEAD --name-only --staged`;
    my @files = split /\n/, $output;

    return @files;
}

sub main {
    make_perltidy_and_whiter();
    check_server_side_whitespaces();
    check_client_side_whitespaces();
    check_javascript();
}

main();
