package App::SCS::Page;

use IO::All;
use Time::Local qw(timelocal);
use List::Util qw(reduce);
use Module::Runtime qw(use_module);
use HTML::Zoom;
use Moo;

has $_ => (is => 'ro', reader => "_$_", init_arg => $_) for qw(page_set);

sub _page_set_class { ref($_[0]->_page_set) }
sub _top_dir { $_[0]->_page_set->top_dir }
sub _my_path { io->dir($_[0]->_top_dir)->catdir($_[0]->path) }

with 'App::SCS::Role::PageChildren';

has $_ => (is => 'ro') for qw(
  title subtitle description keywords  html created path
);

has plugin_config => (is => 'rw', 'required' => 1);

sub has_plugin_config { exists $_[0]->plugin_config->{$_[1]} }

sub with_plugin_config {
  my ($self, $with_name, $with_config) = @_;
  my @orig = @{$self->plugin_config};
  my @new;
  while (my ($name, $config) = splice @orig, 0, 2) {
    push @new, (
      $name eq $with_name
        ? ($name, { %$config, %$with_config })
        : ($name, $config)
    );
  }
  return $self->with(plugin_config => \@new);
}

has _raw_page_plugins => (is => 'lazy', builder => sub {
  my ($self) = @_;
  my $plugin_config = $self->plugin_config;
  my ($plugin_map, $defaults) = @{$self->_page_set->plugin_config}
                                 {qw(plugin_map defaults)};
  my @spec = (@$defaults, @$plugin_config);
  my @plugins;
  while (my ($name, $config) = splice @spec, 0, 2) {
    my $info = $plugin_map->{$name};
    die "No class for plugin name ${name}" unless $info->{class};
    push @plugins,
      use_module($info->{class})->new(
        ($info->{config}||sub{})->(), %$config, page => $self,
        plugin_map => $plugin_map, # some things will need this
      );
  }
  return \@plugins;
});

has _page_plugins => (is => 'lazy', builder => sub {
  my ($self) = @_;
  my $raw = $self->_raw_page_plugins;
  reduce { $b->filter_plugins($a) } $raw, @$raw;
});

sub published_at {
  $_[0]->created
    ? scalar localtime timelocal
        map +(@{$_}[0..3], $_->[4]-1, $_->[5]-1900),
          [ reverse split '\D+', $_[0]->created ]
    : ''
}

sub to_app {
  my ($self) = @_;
  return sub { $self->to_psgi_response(@_) };
}

sub to_psgi_response {
  my ($self, $env) = @_;

  if (my $cb = $env->{'App::SCS::Command::Generate.extra_pages'}) {
    $cb->($_->extra_pages) for @{$self->page_plugins};
  }

  $self->_psgi_response;
}

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

sub _build__psgi_response {
  my ($self) = @_;

  my $psgi_res = [
    200, [ 'Content-type' => 'text/html' ], $self->_content_zoom->to_fh
  ];

  return reduce {
    $b->filter_psgi_response($a)
  } $psgi_res, @{$self->_page_plugins};
}

sub _content_zoom {
  my ($self) = @_;
  return reduce {
    $b->filter_content_zoom($a)
  } $self->_html_zoom, @{$self->_page_plugins};
}

sub _html_zoom {
  my ($self) = @_;
  return reduce {
    $b->filter_html_zoom($a)
  } HTML::Zoom->from_html($self->html), @{$self->_page_plugins};
}

sub body {
  my ($self) = @_;
  HTML::Zoom->from_html($self->html)
            ->collect(body => { into => \my @ev })
            ->run;
  HTML::Zoom->from_events(\@ev)->to_html;
}

no Moo;

sub with {
  my $self = shift;
  return ref($self)->new(%$self, @_);
}

1;
