package App::Clifton::Task;

use Log::Contextual qw(:log);
use Async::MergePoint;
use Moo;

extends 'App::Clifton::Component';

has on_finished => (is => 'rw');

has name => (is => 'ro', required => 1);

has body => (is => 'ro', required => 1);

has guard => (is => 'ro', predicate => 'has_guard');

has args => (is => 'ro', required => 1);

has dependencies => (is => 'ro', default => sub { {} });

has merge_point => (is => 'lazy');

sub _build_merge_point { Async::MergePoint->new }

sub BUILD {
  my ($self) = @_;
  if (my $cb = $self->args->{on_finished}) {
    $self->on_finished($cb);
  }
  my $deps = $self->dependencies;
  if (my @needs = keys %$deps) {
    my $mp = $self->merge_point;
    $mp->needs(@needs);
    foreach my $key (@needs) {
      $deps->{$key}->on_finished(sub { $mp->done($key => $_[0]) });
    }
    $mp->close(on_finished => $self->_capture_weakself('_schedule_body'));
  } else {
    $self->_schedule_body;
  }
}

sub _schedule_body {
  my ($self, %args) = @_;
  my $fire_body = $self->_capture_weakself('_fire_body');
  $args{on_finished} = $self->_finished_callback;
  %args = (%{$self->args}, %args);
  $self->_schedule(sub { $fire_body->(\%args); });
}

sub _fire_body {
  my $self = shift;
  if ($self->has_guard and my ($result) = $self->guard->(@_)) {
    $_[0]->{on_finished}($result);
  } else {
    $self->body->(@_);
  }
}

sub _finished_callback {
  my ($self) = @_;
  $self->_capture_weakself('_schedule_finished');
}

sub _schedule_finished {
  my ($self, @args) = @_;
  if (my $on_finished = $self->on_finished) {
    $self->_schedule(sub { $on_finished->(@args) });
  }
  $self->parent->remove_child($self);
#$self->$::Dwarn;
#warn Devel::FindRef::track $self;
}

1;
