# Object to handle pending system actions

package RWDE::Scheduler::Pending_action;

use strict;
use warnings;

use Error qw(:try);

use RWDE::Exceptions;

use RWDE::Time;

use base qw(RWDE::DB::Record);

our ($db, $table, $index, $id, @fieldnames, @modifiable_fieldnames, %fields, %static_fields, %modifiable_fields, @static_fieldnames);

BEGIN {
  $table = 'pending_actions';
  $id    = 'action_id';
  $index = 'pending_actions_action_id_seq';

  #all of the static fields present in the login table
  %static_fields = (

    # Field => [Type, Descr]
    action_id      => [ 'int',       'Action ID' ],
    action_created => [ 'timestamp', 'Date the action was created' ],
  );

  #all of the fields allowed to be modified in the login table
  %modifiable_fields = (

    # Field => [Type, Descr]
    action_due              => [ 'timestamp', 'Action due date' ],
    action_started          => [ 'timestamp', 'Action start time' ],
    action_completed        => [ 'timestamp', 'Action completed time' ],
    action_type             => [ 'char',      'Class namespace' ],
    action_function         => [ 'char',      'Action function' ],
    action_scheduler_name   => [ 'char',      'Preferred Scheduler' ],
    action_exit_status      => [ 'char',      'Exit Info' ],
    action_params           => [ 'hash',      'Action parameters' ],
    action_repeat_frequency => [ 'char',      'Repeat frequency' ],
    action_priority         => [ 'int',       'Action priority' ],
    action_parent_id        => [ 'int',       'Group actions for identification purpose' ],
    scheduler_id            => [ 'int',       'Taken by scheduler' ],
  );

  %fields = (%static_fields, %modifiable_fields);

  @static_fieldnames     = sort keys %static_fields;
  @modifiable_fieldnames = sort keys %modifiable_fields;
  @fieldnames            = sort keys %fields;
}

## @method object get_db()
# (Enter get_db info here)
# @return
sub get_db {
  my ($self, $params) = @_;

  return 'default';
}

sub create {
  my ($self, $params) = @_;

  my @required = qw( action_type action_function action_params);
  $self->check_params({ required => \@required, supplied => $params });

  my $action = $self->new();

  $action->action_type($$params{action_type});
  $action->action_function($$params{action_function});
  $action->action_params($$params{action_params});
  $action->action_scheduler_name($$params{action_scheduler_name});
  $action->action_due($$params{action_due});

  $action->action_priority($$params{action_priority})  if defined $$params{action_priority};
  $action->action_parent_id($$params{action_parent_id}) if defined $$params{action_parent_id};

  $action->create_record();

  return $action;
}

sub process {
  my ($self, $params) = @_;

  $self->action_started(RWDE::Time->now());
  $self->update_record;

  my $type     = $self->action_type;
  my $function = $self->action_function;

  #build the expression we are going to make
  try {
    my $string = $self->dehashify({ hash => $self->action_params });
    $self->debug_info('devel', "Pending_action::process $type -> $function ($string) ");

    #make the call
    $type->$function($self->action_params);

    $self->action_completed(RWDE::Time->now());
    $self->scheduler_id(0);    #indicates completed action
    $self->action_exit_status('Successful');
    $self->update_record;

    if ($self->is_recurring()) {
      $self->reschedule();
    }

  }

  catch Error with {
    my $ex = shift;

   $self->syslog_msg('warn', $ex);

    RWDE::PostMaster->send_report_message(
      {
        info     => $ex,
        formdata => $self->action_params,
        uri      => 'Pending action doing: ' . $type . '->' . $function,
      }
    );

    #enable re-scheduling for the tasks that explicitly require it
    if ($ex->is_retry() and !$self->is_recurring()) {
      $self->reschedule();
    }

    $self->action_exit_status($ex);
    $self->update_record;
  };

  return ();
}

sub cleanup {
  my ($self, $params) = @_;

  if (defined $self->action_completed) {

    # The action was completed, but the record wasn't released
    # Mark as completed
    $self->scheduler_id(0);
  }

  else {

    # The action has not completed
    # return the action to the pool to be picked up by a new scheduler
    $self->scheduler_id('NULL');
  }

  $self->update_record;

  return ();
}

sub is_recurring {
  my ($self, $params) = @_;

  return (defined $self->action_repeat_frequency);
}

sub reschedule {
  my ($self, $params) = @_;

  my $interval;

  if (defined $self->action_repeat_frequency) {
    $interval = $self->action_repeat_frequency;
  }

  elsif (defined $$params{interval}) {
    $interval = $$params{interval};
  }

  else {
    $interval = '15 minutes';
  }

  my $new_action = $self->new;

  $new_action->action_type($self->action_type);
  $new_action->action_function($self->action_function);
  $new_action->action_params($self->action_params);
  $new_action->action_scheduler_name($self->action_scheduler_name);
  $new_action->action_repeat_frequency($self->action_repeat_frequency);

  my $new_runtime = RWDE::Time->fetch_time({ timestamp => 'NOW()', interval => $interval });
  $new_action->action_due($new_runtime);

  $new_action->create_record();

  return $new_action;
}

sub count_outstanding {
  my ($self, $params) = @_;

  return $self->count_query({ query => "action_due < NOW() AND scheduler_id IS NULL AND action_repeat_frequency IS NULL", });
}

1;
