package System::Introspector::Report::Publish::MediaWiki;
use Moo;
use Try::Tiny;
use Object::Remote;
use Object::Remote::Future;
use Log::Contextual::WarnLogger;
use Log::Contextual qw( :log ),
  -default_logger => Log::Contextual::WarnLogger->new({
    env_prefix => 'SI_REPORT_MEDIAWIKI',
    levels      => [qw( error warn info debug trace )],
  });

use aliased 'System::Introspector::Report::Publish::MediaWiki::Updater';

has page_options => (
  is => 'ro',
  default => sub { {} },
  init_arg => 'page',
);

has process_count => (
  is => 'ro',
  isa => sub {
    die "MediaWiki processes count has to be above 0\n"
      if $_[0] < 1;
  },
  default => sub { 1 },
  init_arg => 'processes',
);

has updater_pool => (
  is => 'lazy',
  init_arg => undef,
);

sub _build_updater_pool {
  my ($self) = @_;
  return [map {
    Updater->new::on('-', $self->connect_info_pairs);
  } 1 .. $self->process_count];
}

sub publish {
  my ($self, $reports) = @_;
  my $pages = $self->page_options;
  log_info {
    sprintf "Pushing reports to MediaWiki at '%s'", $self->api_uri;
  };
  for my $page (sort keys %$pages) {
    my $options = $pages->{$page};
    if ($page =~ m{\%\(meta:(.+?)\)}) {
      $self->_publish_dynamic($reports, $page, $1, $options);
    }
    else {
      $self->_publish_pages([$reports, $page, $options]);
    }
  }
  log_info { "Finished pushing to MediaWiki" };
  return 1;
}

sub _publish_pages {
  my ($self, @pages) = @_;
  my $stream = sub {
    my $next = shift @pages
      or return undef;
    my ($reports, $page_name, $options) = @$next;
    my $sorted = $self->_sort_reports($reports, $options->{report} || []);
    return [$page_name, $options, $sorted];
  };
  await_all map {
    $_->start::process($stream);
  } @{ $self->updater_pool };
}

sub _sort_reports {
  my ($self, $reports, $included) = @_;
  my @matchers = map {
    $self->_prepare_matcher_from($_);
  } ref($included) ? @{$included} : $included;
  my @grouped;
  for my $report (@$reports) {
    my ($group_idx) = grep {
      $self->_match_id($report, $matchers[$_]);
    } 0 .. $#matchers;
    if (defined $group_idx) {
      push @{$grouped[$group_idx]}, $report;
    }
  }
  return [ map { (@$_) } @grouped ];
}

sub _publish_dynamic {
  my ($self, $reports, $page_name, $key, $options) = @_;
  my %reports_by_key;
  for my $report (@$reports) {
    my $value = $report->{meta}{$key};
    next unless defined $value;
    push @{ $reports_by_key{$value} ||= [] }, $report;
  }
  my @pages;
  for my $value (keys %reports_by_key) {
    (my $effective_page = $page_name)
      =~ s{\%\(meta:\Q$key\E\)}{$value};
    my $assoc = $reports_by_key{$value};
    push @pages, [$assoc, $effective_page, $options];
  }
  $self->_publish_pages(@pages);
  return 1;
}

with $_ for qw(
  System::Introspector::Report::Publish::API
  System::Introspector::Report::Publish::MediaWiki::HasConnectInfo
);

1;
