package Minion::Command::minion::worker;
use Mojo::Base 'Mojolicious::Command';

use Mojo::Util qw(getopt);

has description => 'Start Minion worker';
has usage       => sub { shift->extract_usage };

sub run {
  my ($self, @args) = @_;

  my $worker = $self->app->minion->worker;
  my $status = $worker->status;
  getopt \@args,
    'C|command-interval=i'   => \$status->{command_interval},
    'D|dequeue-timeout=i'    => \$status->{dequeue_timeout},
    'dispatch-interval=i'    => \$status->{dispatch_interval},
    'I|heartbeat-interval=i' => \$status->{heartbeat_interval},
    'j|jobs=i'               => \$status->{jobs},
    'L|limit=s' => sub { $_[1] =~ /^(\S+)=(\d+)$/ ? ($status->{limits}{$1} = $2) : warn "Invalid limit option: $_[1]" },
    'q|queue=s' => \my @queues,
    'R|repair-interval=i'    => \$status->{repair_interval},
    's|spare=i'              => \$status->{spare},
    'S|spare-min-priority=i' => \$status->{spare_min_priority};
  $status->{queues} = \@queues if @queues;

  my $log = $self->app->log;
  $log->info("Worker $$ started");
  $worker->on(dequeue => sub { pop->once(spawn => \&_spawn) });
  $worker->run;
  $log->info("Worker $$ stopped");
}

sub _spawn {
  my ($job, $pid)  = @_;
  my ($id,  $task) = ($job->id, $job->task);
  $job->app->log->debug(qq{Process $pid is performing job "$id" with task "$task"});
}

1;

=encoding utf8

=head1 NAME

Minion::Command::minion::worker - Minion worker command

=head1 SYNOPSIS

  Usage: APPLICATION minion worker [OPTIONS]

    ./myapp.pl minion worker
    ./myapp.pl minion worker -m production -I 15 -C 5 -R 3600 -j 10
    ./myapp.pl minion worker -q important -q default
    ./myapp.pl minion worker -L foo=2 -L bar=5

  Options:
    -C, --command-interval <seconds>     Worker remote control command interval,
                                         defaults to 10
    -D, --dequeue-timeout <seconds>      Maximum amount of time to wait for
                                         jobs, defaults to 5
        --dispatch-interval <seconds>    Interval for dispatching due
                                         schedules, defaults to 30 (set to 0
                                         to disable on this worker)
    -h, --help                           Show this summary of available options
        --home <path>                    Path to home directory of your
                                         application, defaults to the value of
                                         MOJO_HOME or auto-detection
    -I, --heartbeat-interval <seconds>   Heartbeat interval, defaults to 300
    -j, --jobs <number>                  Maximum number of jobs to perform
                                         parallel in forked worker processes
                                         (not including spare processes),
                                         defaults to 4
    -L, --limit <name>=<number>          Limit number of jobs for a specific
                                         task
    -m, --mode <name>                    Operating mode for your application,
                                         defaults to the value of
                                         MOJO_MODE/PLACK_ENV or "development"
    -q, --queue <name>                   One or more queues to get jobs from,
                                         defaults to "default"
    -R, --repair-interval <seconds>      Repair interval, up to half of this
                                         value can be subtracted randomly to
                                         make sure not all workers repair at the
                                         same time, defaults to 21600 (6 hours)
    -s, --spare <number>                 Number of spare worker processes to
                                         reserve for high priority jobs,
                                         defaults to 1
    -S, --spare-min-priority <number>    Minimum priority of jobs to use spare
                                         worker processes for, defaults to 1

=head1 DESCRIPTION

L<Minion::Command::minion::worker> starts a L<Minion> worker. You can have as many workers as you like.

=head1 WORKER SIGNALS

The L<Minion::Command::minion::worker> process can be controlled at runtime with the following signals.

=head2 INT, TERM

Stop gracefully after finishing the current jobs.

=head2 QUIT

Stop immediately without finishing the current jobs.

=head1 JOB SIGNALS

The job processes spawned by the L<Minion::Command::minion::worker> process can be controlled at runtime with the
following signals.

=head2 INT, TERM

This signal starts out with the operating system default and allows for jobs to install a custom signal handler to stop
gracefully.

=head2 USR1, USR2

These signals start out being ignored and allow for jobs to install custom signal handlers.

=head1 REMOTE CONTROL COMMANDS

The L<Minion::Command::minion::worker> process can be controlled at runtime through L<Minion::Command::minion::job>,
from anywhere in the network, by broadcasting the following remote control commands.

=head2 jobs

  $ ./myapp.pl minion job -b jobs -a '[10]'
  $ ./myapp.pl minion job -b jobs -a '[10]' 23

Instruct one or more workers to change the number of jobs to perform concurrently. Setting this value to C<0> will
effectively pause the worker. That means all current jobs will be finished, but no new ones accepted, until the number
is increased again. Note that L</"spare"> might need to be adjusted if high priority jobs should be paused as well.

=head2 kill

  $ ./myapp.pl minion job -b kill -a '["INT", 10025]'
  $ ./myapp.pl minion job -b kill -a '["INT", 10025]' 23

Instruct one or more workers to send a signal to a job that is currently being performed. This command will be ignored
by workers that do not have a job matching the id. That means it is safe to broadcast this command to all workers.

=head2 spare

  $ ./myapp.pl minion job -b spare -a '[1]'
  $ ./myapp.pl minion job -b spare -a '[1]' 23

Instruct one or more workers to change the number of spare worker processes to reserve for high priority jobs. Setting
this value to C<0> will effectively disable the feature, this is required to pause the worker. That means all current
high priority jobs will be finished, but no new ones accepted, until the number is increased again.

=head2 stop

  $ ./myapp.pl minion job -b stop -a '[10025]'
  $ ./myapp.pl minion job -b stop -a '[10025]' 23

Instruct one or more workers to stop a job that is currently being performed immediately. This command will be ignored
by workers that do not have a job matching the id. That means it is safe to broadcast this command to all workers.

=head1 ATTRIBUTES

L<Minion::Command::minion::worker> inherits all attributes from L<Mojolicious::Command> and implements the following
new ones.

=head2 description

  my $description = $worker->description;
  $worker         = $worker->description('Foo');

Short description of this command, used for the command list.

=head2 usage

  my $usage = $worker->usage;
  $worker   = $worker->usage('Foo');

Usage information for this command, used for the help screen.

=head1 METHODS

L<Minion::Command::minion::worker> inherits all methods from L<Mojolicious::Command> and implements the following new
ones.

=head2 run

  $worker->run(@ARGV);

Run this command.

=head1 SEE ALSO

L<Minion>, L<Minion::Guide>, L<https://minion.pm>, L<Mojolicious::Guides>, L<https://mojolicious.org>.

=cut
