package DX::Hypothesis;

use DX::ActionPolicy::LockScope;
use Types::Standard qw(ArrayRef);
use DX::Utils qw(deparse);
use DX::Class;

has scope => (is => 'ro', isa => Scope, required => 1);

has resolved_propositions => (
  is => 'ro', isa => ResolvedPropositionSet, required => 1
);

has outstanding_propositions => (
  is => 'ro', isa => ArrayRef[Proposition], required => 1
);

has actions => (
  is => 'ro', isa => ArrayRef[Action], required => 1
);

has action_applications => (
  is => 'ro', isa => ArrayRef[Action], required => 1
);

has action_policy => (is => 'ro', isa => ActionPolicy, required => 1);

sub head_proposition { shift->outstanding_propositions->[0] }

sub with_actions {
  my ($self, @actions) = @_;
  my $hyp = $self;
  foreach my $act (@actions) {
    return undef unless $self->action_policy->allows($act);
    ($hyp, my @events) = $act->dry_run($hyp);
    return undef unless $hyp;
    $hyp = $hyp->but_recheck_for(@events);
    return undef unless $hyp;
  }
  return $hyp;
}

sub but_recheck_for {
  my ($self, @events) = @_;
  my ($still_resolved, @recheck) = $self->resolved_propositions
                                        ->but_expire_for(@events);
  return $self unless @recheck;

  my $ap = DX::ActionPolicy::LockScope->new(
    lock_to_depth => $self->scope->depth,
    next_policy => $self->action_policy,
  );

  # we should probably be doing something about pruning the scope
  # but that's completely pointless until we have rules

  my $hyp = ref($self)->new(
    scope => $self->scope,
    resolved_propositions => DX::ResolvedPropositionSet->new_empty,
    outstanding_propositions => \@recheck,
    actions => [],
    action_applications => [],
    action_policy => $ap,
  );

  my $ss = DX::SearchState->new_for($hyp);

  return undef unless my $sol_ss = $ss->find_solution;

  my $sol_rps = $sol_ss->current_hypothesis->resolved_propositions;

  my $rps = $still_resolved;

  $rps = $rps->with_updated_dependencies_for(
    $_, $sol_rps->dependencies_for($_)
  ) for @recheck;

  return $self->but(resolved_propositions => $rps);
}

sub resolve_head_dependent_on {
  my ($self, $depends) = @_;
  my ($first, @rest) = @{$self->outstanding_propositions};
  $self->but(
    resolved_propositions => $self->resolved_propositions
                                  ->with_resolution_for(
                                      $first,
                                      $depends,
                                    ),
    outstanding_propositions => \@rest,
  );
}

1;
