package DX::Solver;

use DX::RuleSet;
use DX::State;
use DX::ResultStream;
use List::Util qw(reduce);
use Moo;

has rule_set => (
  is => 'lazy',
  handles => [ qw(add_predicate add_rule) ],
  builder => sub {
    DX::RuleSet->new
  },
);

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

has observation_policy => (is => 'ro');

sub query {
  my ($self, $vars, @terms) = @_;
  my $rule_set = $self->rule_set;
  push @terms, map +[ materialize => $_ ], @$vars;
  my $head = $rule_set->expand_and_link(undef, @terms);
  my $state = DX::State->new(
    next_op => $head,
    return_stack => [],
    by_id => {},
    scope => {},
    last_choice => [],
    facts => $self->facts,
    rule_set => $rule_set,
  )->assign_vars(map +($_ => {}), @$vars);
  return DX::ResultStream->new(
    for_state => $state,
    ($self->observation_policy
      ? (observation_policy => $self->observation_policy)
      : ()),
  );
}

sub run_action {
  my ($self, $action) = @_;
  my @invalidate = $action->run;
  while (my ($type, $value) = splice @invalidate, 0, 2) {
    $self->facts->{$type}->remove_value($value);
  }
}

1;
