package IO::Iron::IronWorker::Task;

## no critic (Documentation::RequirePodAtEnd)
## no critic (Documentation::RequirePodSections)
## no critic (ControlStructures::ProhibitPostfixControls)
## no critic (Subroutines::RequireArgUnpacking)

use 5.010_000;
use strict;
use warnings;

# Global creator
BEGIN {
}

# Global destructor
END {
}


# ABSTRACT: IronWorker (Online Worker Platform) Client (Task).

our $VERSION = '0.13'; # VERSION: generated by DZP::OurPkgVersion



use Log::Any  qw($log);
use Hash::Util 0.06 qw{lock_keys unlock_keys};
use Carp::Assert::More;
use English '-no_match_vars';
use Params::Validate qw(:all);

use IO::Iron::IronWorker::Api ();

# CONSTANTS for this module

# DEFAULTS
#my $DEFAULT_DELAY = 0;
#my $DEFAULT_TIMEOUT = 3600;
#my $DEFAULT_PRIORITY = 0;


sub new { ## no critic (Subroutines::ProhibitExcessComplexity)
	my ($class, $params) = @_;
	$log->tracef('Entering new(%s, %s)', $class, $params);
	my $self;
	my @self_keys = ( ## no critic (CodeLayout::ProhibitQuotedWordLists)
		'ironworker_client', # Reference to IronWorker client
		'connection',      # Reference to REST client
		'last_http_status_code', # After successfull network operation, the return value is here.
		# Can be given when queueing a new task:
		'code_name', # The name of the code package to execute for this task (mandatory).
		'payload',         # A string of data to be passed to the worker (usually JSON), can be empty (mandatory).
		'priority',        # The priority queue to run the task in. Valid values are 0, 1, and 2. 0 is the default.
		'timeout',         # The maximum runtime of your task in seconds.
		'delay',           # The number of seconds to delay before actually queuing the task. Default is 0.
		'name',            # Name of task or scheduled task.
		# These are for scheduled task:
		'run_every',       # The amount of time, in seconds, between runs
		'end_at',          # The time tasks will stop being queued. Should be a time or datetime.
		'run_times',       # The number of times a task will run.
		'start_at',        # The time the scheduled task should first be run.
		# Returned when queried a queued task:
		'id',              # Task or Scheduled task id.
		'project_id',      # Iron.io project ID.
		'code_id',         # The code package id.
		'status',          # Task execution status.
		'code_history_id', # Code package revision id?
		'code_rev',        # Code package revision number.
		'start_time',      # Execution started?
		'end_time',        # Execution finished?
		'duration',        # Execution duration?
		'updated_at',      # Timestamp (ISO) of last update.
		'created_at',      # Timestamp (ISO) of creation. E.g. "2012-11-10T18:31:08.064Z"
	);
	lock_keys(%{$self}, @self_keys);
	$self->{'ironworker_client'} = $params->{'ironworker_client'} if defined $params->{'ironworker_client'};
	$self->{'connection'} = $params->{'connection'} if defined $params->{'connection'};
	assert_isa( $self->{'connection'}, 'IO::Iron::Connection', 'self->{\'connection\'} is IO::Iron::Connection.' );
	assert_isa( $self->{'ironworker_client'}, 'IO::Iron::IronWorker::Client', 'self->{\'ironworker_client\'} is IO::Iron::IronWorker::Client.' );

	$self->{'code_name'} = $params->{'code_name'};
	$self->{'payload'} = $params->{'payload'};
	$self->{'priority'} = $params->{'priority'} if defined $params->{'priority'};
	$self->{'timeout'} = $params->{'timeout'}  if defined $params->{'timeout'};
	$self->{'delay'} = $params->{'delay'} if defined $params->{'delay'};

	$self->{'run_every'} = $params->{'run_every'} if defined $params->{'run_every'};
	$self->{'end_at'} = $params->{'end_at'} if defined $params->{'end_at'};
	$self->{'run_times'} = $params->{'run_times'} if defined $params->{'run_times'};
	$self->{'start_at'} = $params->{'start_at'} if defined $params->{'start_at'};

	$self->{'id'} = $params->{'id'} if defined $params->{'id'};
	$self->{'project_id'} = $params->{'project_id'} if defined $params->{'project_id'};
	$self->{'code_id'} = $params->{'code_id'} if defined $params->{'code_id'};
	$self->{'status'} = $params->{'status'} if defined $params->{'status'};
	$self->{'code_history_id'} = $params->{'code_history_id'} if defined $params->{'code_history_id'};
	$self->{'code_rev'} = $params->{'code_rev'} if defined $params->{'code_rev'};
	$self->{'start_time'} = $params->{'start_time'} if defined $params->{'start_time'};
	$self->{'end_time'} = $params->{'end_time'} if defined $params->{'end_time'};
	$self->{'duration'} = $params->{'duration'} if defined $params->{'duration'};
	$self->{'updated_at'} = $params->{'updated_at'} if defined $params->{'updated_at'};
	$self->{'created_at'} = $params->{'created_at'} if defined $params->{'created_at'};
	$self->{'name'} = $params->{'name'} if defined $params->{'name'};

	# All of the above can be undefined, except the codename and payload.
	assert_nonblank( $self->{'code_name'}, 'code_name is defined and is not blank.' );
	assert_defined( $self->{'payload'}, 'payload is defined, can be blank.' );
	# If priority, timeout or delay are undefined, the IronWorker defaults (at the server) will be used.

	unlock_keys(%{$self});
	my $blessed_ref = bless $self, $class;
	lock_keys(%{$self}, @self_keys);

	$log->tracef('Exiting new: %s', $blessed_ref);
	return $blessed_ref;
}


sub code_name { return $_[0]->_access_internal('code_name', $_[1]); }
sub payload { return $_[0]->_access_internal('payload', $_[1]); }
sub priority { return $_[0]->_access_internal('priority', $_[1]); }
sub timeout { return $_[0]->_access_internal('timeout', $_[1]); }
sub delay { return $_[0]->_access_internal('delay', $_[1]); }
sub name { return $_[0]->_access_internal('name', $_[1]); }
		# These are for scheduled task:
sub run_every { return $_[0]->_access_internal('run_every', $_[1]); }
sub end_at { return $_[0]->_access_internal('end_at', $_[1]); }
sub run_times { return $_[0]->_access_internal('run_times', $_[1]); }
sub start_at { return $_[0]->_access_internal('start_at', $_[1]); }
		# Returned when queried a queued task:
sub id { return $_[0]->_access_internal('id', $_[1]); }
sub project_id { return $_[0]->_access_internal('project_id', $_[1]); }
sub code_id { return $_[0]->_access_internal('code_id', $_[1]); }
sub status { return $_[0]->_access_internal('status', $_[1]); }
sub code_history_id { return $_[0]->_access_internal('code_history_id', $_[1]); }
sub code_rev { return $_[0]->_access_internal('code_rev', $_[1]); }
sub start_time { return $_[0]->_access_internal('start_time', $_[1]); }
sub end_time { return $_[0]->_access_internal('end_time', $_[1]); }
sub duration { return $_[0]->_access_internal('duration', $_[1]); }
sub updated_at { return $_[0]->_access_internal('updated_at', $_[1]); }
sub created_at { return $_[0]->_access_internal('created_at', $_[1]); }

sub _access_internal {
	my ($self, $var_name, $var_value) = @_;
	$log->tracef('_access_internal(%s, %s)', $var_name, $var_value);
	if( defined $var_value ) {
		$self->{$var_name} = $var_value;
		return $self;
	}
	else {
		return $self->{$var_name};
	}
}


sub log { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
	my ($self) = @_;
	$log->tracef('Entering log().');

	my $task_id = $self->id();
	assert_nonblank($task_id, 'task id not set. Task queued yet?');
	my $connection = $self->{'connection'};
	my ( $http_status_code, $response_message ) =
		$connection->perform_iron_action(
		IO::Iron::IronWorker::Api::IRONWORKER_GET_A_TASKS_LOG(),
		{ '{Task ID}' => $task_id, } );
	$self->{'last_http_status_code'} = $http_status_code;

	$log->tracef( 'Exiting log(): %s', $response_message );
	return $response_message;
}


sub cancel {
	my ($self) = @_;
	$log->tracef('Entering cancel().');

	my $task_id = $self->id();
	assert_nonblank($task_id, 'task id not set. Task queued yet?');
	my $connection = $self->{'connection'};
	my ( $http_status_code, $response_message ) =
		$connection->perform_iron_action(
		IO::Iron::IronWorker::Api::IRONWORKER_CANCEL_A_TASK(),
		{ '{Task ID}' => $task_id, } );
	$self->{'last_http_status_code'} = $http_status_code;
	assert_is($response_message->{'msg'}, 'Cancelled');

	$log->tracef( 'Exiting cancel(): %s', 1 );
	return 1;
}


sub set_progress {
	my $self = shift;
	my %params = validate_with(
		'params' => \@_,
		'normalize_keys' => sub { return lc shift },
		'spec' => {
			'percent' => { type => SCALAR, }, # percentage.
			'msg' => { type => SCALAR, }, # message.
		},
    );
	$log->tracef('Entering set_progress(%s)', \%params);

	my $task_id = $self->id();
	assert_nonblank($task_id, 'task id not set. Task queued yet?');
	my $connection = $self->{'connection'};
	my ( $http_status_code, $response_message ) = $connection->perform_iron_action(
		IO::Iron::IronWorker::Api::IRONWORKER_SET_A_TASKS_PROGRESS(),
		{ '{Task ID}' => $task_id,
			'body' => \%params,
		}
	);
	$self->{'last_http_status_code'} = $http_status_code;
	assert_is($response_message->{'msg'}, 'Progress set');

	$log->tracef( 'Exiting set_progress(): %s', 1 );
	return 1;
}


sub retry {
	my $self = shift;
	my %params = validate_with(
		'params' => \@_,
		'normalize_keys' => sub { return lc shift },
		'spec' => {
			'delay' => { type => SCALAR, }, # delay
		},
    );
	$log->tracef( 'Entering retry(%s)', \%params );

	my $task_id = $self->id();
	assert_nonblank($task_id, 'task id not set. Task queued yet?');
	my $connection = $self->{'connection'};
	my ( $http_status_code, $response_message ) = $connection->perform_iron_action(
		IO::Iron::IronWorker::Api::IRONWORKER_RETRY_A_TASK(),
		{
			'{Task ID}' => $task_id,
			'body' => \%params,
		}
	);
	$self->{'last_http_status_code'} = $http_status_code;
	assert_is($response_message->{'msg'}, 'Queued up');
	my $new_task_id = $response_message->{'tasks'}->[0]->{'id'};
	$self->id($new_task_id); # We get a new id.

	$log->tracef( 'Exiting retry(): %s', $new_task_id );
	return $new_task_id;
}


sub cancel_scheduled {
	my ($self) = @_;
	$log->tracef('Entering cancel_scheduled().');

	my $task_id = $self->id();
	assert_nonblank($task_id, 'task id not set. Task scheduled yet?');
	my $connection = $self->{'connection'};
	my ( $http_status_code, $response_message ) =
		$connection->perform_iron_action(
		IO::Iron::IronWorker::Api::IRONWORKER_CANCEL_A_SCHEDULED_TASK(),
		{ '{Schedule ID}' => $task_id, } );
	$self->{'last_http_status_code'} = $http_status_code;
	assert_is($response_message->{'msg'}, 'Cancelled');

	$log->tracef( 'Exiting cancel_scheduled(): %s', 1 );
	return 1;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

IO::Iron::IronWorker::Task - IronWorker (Online Worker Platform) Client (Task).

=head1 VERSION

version 0.13

=head1 SYNOPSIS

Please see IO::Iron::IronWorker::Client for usage.

=for stopwords IronWorker API Mikko Koivunalho perldoc CPAN AnnoCPAN ACKNOWLEDGMENTS TODO

=for stopwords JSON runtime timestamp io Timestamp Params IronHTTPCallException STDOUT params msg

=head1 REQUIREMENTS

=head1 SUBROUTINES/METHODS

=head2 new

Creator function.

=head2 Getters/setters

Set or get a property.
When setting, returns the reference to the object.

=over 8

=item code_name,         The name of the code package to execute for this task (mandatory).

=item payload,          A string of data to be passed to the worker (usually JSON), can be empty (mandatory).

=item priority,         The priority queue to run the task in. Valid values are 0, 1, and 2. 0 is the default.

=item timeout,          The maximum runtime of your task in seconds.

=item delay,            The number of seconds to delay before actually queuing the task. Default is 0.

=item name,             Name of task or scheduled task.

=item B<These are for scheduled task:>

=item run_every,        The amount of time, in seconds, between runs

=item end_at,           The time tasks will stop being queued. Should be a time or a full timestamp (date and time).

=item run_times,        The number of times a task will run.

=item start_at,         The time the scheduled task should first be run.

=item B<Returned when queried a queued task:>

=item id,               Task or Scheduled task id.

=item project_id,       Iron.io project ID.

=item code_id,          The code package id.

=item status,           Task execution status.

=item code_history_id,  Code package revision id?

=item code_rev,         Code package revision number.

=item start_time,       Execution started?

=item end_time,         Execution finished?

=item duration,         Execution duration?

=item updated_at,       Timestamp (ISO) of last update.

=item created_at,       Timestamp (ISO) of creation. E.g. "2012-11-10T18:31:08.064Z"

=back

=head2 log

=over

=item Params: [none]

=item Return: task log (text/plain).

=item Exception: IronHTTPCallException if fails. (IronHTTPCallException: status_code=<HTTP status code> response_message=<response_message>)

=back

Return the task's log (task's STDOUT).

=head2 cancel

=over

=item Params: [none]

=item Return: 1 if successful.

=item Exception: IronHTTPCallException if fails. (IronHTTPCallException: status_code=<HTTP status code> response_message=<response_message>)

=back

Cancel a task.

=head2 set_progress

=over

=item Params (in params hash): percent (integer), msg (free text)

=item Return: 1 if successful.

=item Exception: IronHTTPCallException if fails. (IronHTTPCallException: status_code=<HTTP status code> response_message=<response_message>)

=back

Set the progress info to the task.

=head2 retry

=over

=item Params: delay (number of seconds before the task is queued again)

=item Return: new task id if successful.

=item Exception: IronHTTPCallException if fails. (IronHTTPCallException: status_code=<HTTP status code> response_message=<response_message>)

=back

Retry a task. A new task id is updated to id field of the object. The id is also returned.

=head2 cancel_scheduled

=over

=item Params: [none]

=item Return: 1 if successful.

=item Exception: IronHTTPCallException if fails. (IronHTTPCallException: status_code=<HTTP status code> response_message=<response_message>)

=back

Cancel a task.

=head1 AUTHOR

Mikko Koivunalho <mikko.koivunalho@iki.fi>

=head1 BUGS

Please report any bugs or feature requests to bug-io-iron@rt.cpan.org or through the web interface at:
 http://rt.cpan.org/Public/Dist/Display.html?Name=IO-Iron

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Mikko Koivunalho.

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

The full text of the license can be found in the
F<LICENSE> file included with this distribution.

=cut
