package FusionInventory::Agent::Task::Deploy::ActionProcessor::Action::Cmd;

use strict;
use warnings;

use Fcntl qw(SEEK_SET);
use UNIVERSAL::require;

use English qw(-no_match_vars);

sub _evaluateRet {
    my ($retChecks, $buf, $exitStatus) = @_;

    if (ref($retChecks) ne 'ARRAY') {
        return [ 1, 'ok, no check to evaluate.' ];
    }

    foreach my $retCheck (@$retChecks) {

        if ($retCheck->{type} eq 'okCode') {
            foreach (@{$retCheck->{values}}) {
                if ($exitStatus == $_) {
                    return [ 1, "exit status is ok: $_" ];
                }
            }
        } elsif ($retCheck->{type} eq 'okPattern') {
            foreach (@{$retCheck->{values}}) {
                next unless length($_);
                if ($$buf =~ /$_/) {
                    return [ 1, "ok pattern found in log: /$_/" ];
                }
            }
        } elsif ($retCheck->{type} eq 'errorCode') {
            foreach (@{$retCheck->{values}}) {
                if ($exitStatus == $_) {
                    return [ 0, "exit status is not ok: `$_'" ];
                }
            }
        } elsif ($retCheck->{type} eq 'errorPattern') {
            foreach (@{$retCheck->{values}}) {
                next unless length($_);
                if ($$buf =~ /$_/) {
                    return [ 0, "error pattern found in log: /$_/" ];
                }
            }
        }
    }
    return [ 0, '' ];
}

sub _runOnUnix {
    my ($params, $logger) = @_;

    my $buf = `$params->{exec} 2>&1` || '';
    my $errMsg = "$ERRNO";

    # CHILD_ERROR could be negative if exec command is not found, in that case
    # We shoudl report exitStatus as if it was started from shell
    my $exitStatus = $CHILD_ERROR < 0 ? 127 : $CHILD_ERROR >> 8;

    $logger->debug2("Run: ".$buf);

    return ($buf, $errMsg, $exitStatus);
}

sub _runOnWindows {
    my ($params, $logger) = @_;

    FusionInventory::Agent::Tools::Win32->require;

    my ($exitcode, $fd) = FusionInventory::Agent::Tools::Win32::runCommand(
        command => $params->{exec}
    );

    $fd->seek(0, SEEK_SET);

    my $buf;
    while(my $line = readline($fd)) {
        $buf .= $line;
    }
    close $fd;
    $logger->debug2("Run: ".$buf);

    my $errMsg = '';
    if ($exitcode eq '293') {
        $errMsg = "timeout";
    }

    return ($buf, $errMsg, $exitcode);
}


sub do {
    my ($params, $logger) = @_;
    return { 0, ["Internal agent error"]} unless $params->{exec};

    my %envsSaved;

    if ($params->{envs}) {
        foreach my $key (keys %{$params->{envs}}) {
            $envsSaved{$key} = $ENV{$key};
            $ENV{$key} = $params->{envs}{$key};
        }
    }

    my $buf;
    my $errMsg;
    my $exitStatus;

    if ($OSNAME eq 'MSWin32') {
        ($buf, $errMsg, $exitStatus) = _runOnWindows(@_);
    } else {
        ($buf, $errMsg, $exitStatus) = _runOnUnix(@_);
    }

    my $logLineLimit = defined($params->{logLineLimit}) ?
        $params->{logLineLimit} : 10 ;

    my @msg;
    if($buf) {
        my @lines = split('\n', $buf);
        foreach my $line (reverse @lines) {
            chomp($line);
            unshift @msg, $line;
            # Empty lines are kept for local debugging but without updating logLineLimit
            next unless $line;
            last unless --$logLineLimit;
        }
    }

    # Use the retChecks key to know if the command exec is successful
    my $t = _evaluateRet ($params->{retChecks}, \$buf, $exitStatus);

    my $status = $t->[0];
    push @msg, "--------------------------------";
    push @msg, "error msg: `$errMsg'" if $errMsg;
    push @msg, "exit status: `$exitStatus'";
    push @msg, $t->[1];

    # Finally insert header showing started command
    unshift @msg, "================================";
    unshift @msg,  "Started cmd: ".$params->{exec};
    unshift @msg, "================================";

    foreach (@msg) {
        $logger->debug($_);
    }
    $logger->debug("final status: ".$status);

    if ($params->{envs}) {
        foreach my $key (keys %envsSaved) {
            $ENV{$key} = $envsSaved{$key};
        }
    }

    return {
        status => $status,
        msg => \@msg,
    }
}

1;
