use Modern::Perl;
package Orbital::Transfer::Runner::Default;
# ABSTRACT: Default runner
$Orbital::Transfer::Runner::Default::VERSION = '0.001';
use Mu;
use Orbital::Transfer::Common::Setup;
use Capture::Tiny ();
use File::chdir;
use aliased 'Orbital::Transfer::Runnable::Sudo';

use IO::Async::Loop;
use IO::Async::Function;
use IO::Async::Timer::Periodic;

use Orbital::Transfer::System::Docker;

lazy loop => method() {
	my $loop = IO::Async::Loop->new;

	$loop->add( $self->system_function );

	$loop->add( $self->timer );

	$loop;
};

lazy system_function => method() {
	my $function = IO::Async::Function->new(
		code => \&_system_with_env,
	);
};

sub _system_with_env {
	my ( $env, $command, $pwd, $user_args ) = @_;

	use File::chdir;
	local $CWD = $pwd;
	local %ENV = %{ $env };

	local $( = $user_args->{group};
	local $) = $(;

	local $< = $user_args->{user};
	local $> = $<;

	my $exit = CORE::system( @{ $command } );
}

method _system_with_env_args( $runnable ) {
	my $env = $runnable->environment->environment_hash;
	my $user_args = { user => $> , group => (split(' ', $) ))[0] };

	if( ! $runnable->admin_privilege && Orbital::Transfer::System::Docker->is_inside_docker ) {
		# become a non-root user
		$user_args->{group} = '1000';
		$user_args->{user} = 1000;
		$env->{HOME} = '/home/notroot';
	}
	return (
		$env,
		$runnable->command,
		$CWD,
		$user_args,
	);
}

lazy timer => method() {
	my $timer = IO::Async::Timer::Periodic->new(
		interval => 60,

		on_tick => sub {
			print STDERR "SYSTEM KEEP-ALIVE.\n";
		},
	);
};

method _to_sudo($runnable) {
	if( $runnable->admin_privilege ) {
		if( ! Sudo->is_admin_user ) {
			if( Sudo->has_sudo_command
				&& Sudo->sudo_does_not_require_password ) {

				$runnable = Sudo->to_sudo_runnable( $runnable );
			} else {
				warn "Not running command (requires admin privilege): @{ $runnable->command }";
			}
		}
	}

	return $runnable;
}

method system_sync( $runnable, :$log = 1 ) {
	$runnable = $self->_to_sudo( $runnable );

	my $exit;

	say STDERR "Running command @{ $runnable->command }" if $log;
	$exit = _system_with_env( $self->_system_with_env_args(
		$runnable
	));

	if( $exit != 0 ) {
		die "Command '@{ $runnable->command }' exited with $exit";
	}

	return $exit;
}

method system( $runnable ) {
	$runnable = $self->_to_sudo( $runnable );

	my $loop = $self->loop;

	$self->timer->start;

	my $exit;

	say STDERR "Running command @{ $runnable->command }";
	$self->system_function->call(
		args => [ $self->_system_with_env_args( $runnable ) ],
		on_return => sub { $exit = shift },
		on_error => sub { },
	);

	$loop->loop_once while ! defined $exit;

	$self->timer->stop;

	if( $exit != 0 ) {
		die "Command '@{ $runnable->command }' exited with $exit";
	}

	return $exit;
}

method capture( $runnable ) {
	say STDERR "Running command @{ $runnable->command }";
	my $died = 0;
	my $error;
	my @output = Capture::Tiny::capture(sub {
		try {
			$self->system_sync( $runnable, log => 0 );
		} catch {
			$died = 1;
			$error = $_;
		};
	});
	if( $died ) {
		my $stdout = shift @output;
		my $stderr = shift @output;
		die <<EOF;
$error

STDOUT:\n==\n$stdout\n==\n
STDERR:\n==\n$stderr\n==\n

@output
EOF
	}

	wantarray ? @output : $output[0];
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Orbital::Transfer::Runner::Default - Default runner

=head1 VERSION

version 0.001

=head1 AUTHOR

Zakariyya Mughal <zmughal@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Zakariyya Mughal.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut