The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use Wiki::Toolkit::Store::Database;
use Wiki::Toolkit::TestLib;
use DBI;
use Test::More;
use Time::Piece;
use Time::Seconds;

if ( scalar @Wiki::Toolkit::TestLib::wiki_info == 0 ) {
    plan skip_all => "no backends configured";
} else {
    plan tests => ( 10 * scalar @Wiki::Toolkit::TestLib::wiki_info );
}

# These tests are for the "new_only" parameter to ->list_recent_changes.
# We set things up so that we have the following nodes:
# - Ae Pub, in the Pubs category, added 8 days ago, edited 2 days ago and today
# - Ae Bar, in the Bars category, added 5 days ago, edited today
# - Ae Restaurant, in the Restaurants category, added today
# - Ae Nother Pub, in the Pubs category, added today
# Also, make it so the categories of these things were only added today.

my $iterator = Wiki::Toolkit::TestLib->new_wiki_maker;

while ( my $wiki = $iterator->new_wiki ) {
    # Store the time now, so we have a timestamp which precedes "today"'s
    # additions and edits.
    my $start_time = time;
    my $slept = sleep(2);
    warn "Slept for less than a second; test results may be unreliable"
        unless $slept >= 1;

    # Set up the data and run the tests.
    setup_nodes( wiki => $wiki );

    my @nodes = $wiki->list_recent_changes(
        days => 4,
    );
    my @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names,
               [ "Ae Bar", "Ae Nother Pub", "Ae Pub", "Ae Restaurant" ],
               "all nodes returned when new_only omitted" );

    @nodes = $wiki->list_recent_changes(
        days     => 4,
        new_only => 0,
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names,
               [ "Ae Bar", "Ae Nother Pub", "Ae Pub", "Ae Restaurant" ],
               "...and when it's set to false" );

    @nodes = $wiki->list_recent_changes(
        days     => 4,
        new_only => 1,
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Nother Pub", "Ae Restaurant" ],
       "nodes edited but not added in last n days are omitted with new_only" );

    @nodes = $wiki->list_recent_changes(
        between_days => [ 1, 6 ],
        include_all_changes => 1, # to make between_days work - bug?
        new_only     => 1,
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Bar" ],
               "...and this works for between_days too" );

    @nodes = $wiki->list_recent_changes(
        since    => $start_time,
        new_only => 1,
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Nother Pub", "Ae Restaurant" ],
               "...and for since" );

    @nodes = $wiki->list_recent_changes(
        last_n_changes => 3,
        new_only       => 1,
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Bar", "Ae Nother Pub", "Ae Restaurant" ],
               "...and for last n" );

    @nodes = $wiki->list_recent_changes(
        days        => 2,
        new_only    => 1,
        metadata_is => { category => "Pubs" },
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Nother Pub" ],
               "combination of days and metadata_is omits things edited but "
               . "not added in recent days" );

    # Ae Bar wasn't in the Bars category when it was added, but it is now.
    @nodes = $wiki->list_recent_changes(
        days           => 6,
        new_only       => 1,
        metadata_is    => { category => "Bars" },
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Bar" ],
               "...and includes the things it should include" );

    @nodes = $wiki->list_recent_changes(
        days           => 6,
        new_only       => 1,
        metadata_wasnt => { category => "Bars" },
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Bar", "Ae Nother Pub", "Ae Restaurant" ],
               "...and metadata_wasnt works too" );

    # Ae Nother Pub was the only pub added to the Pubs category on creation.
    @nodes = $wiki->list_recent_changes(
        days           => 10,
        new_only       => 1,
        metadata_was   => { category => "Pubs" },
    );
    @names = sort map { $_->{name} } @nodes;
    is_deeply( \@names, [ "Ae Nother Pub" ],
               "combination of new_only and metadata_was omits things which "
               . "didn't have the relevant data when they were created" );

}

sub get_timestamp {
    my %args = @_;
    my $days = $args{days};
    my $now = localtime; # overloaded by Time::Piece
    my $time = $now - ( $days * ONE_DAY );
    return Wiki::Toolkit::Store::Database->_get_timestamp( $time );
}

sub setup_nodes {
    my %args = @_;
    my $wiki = $args{wiki};

    # For "Ae Pub", write directly to the database so we can fake having
    # written something in the past (white box testing).  It might be a good
    # idea at some point to factor this out into Wiki::Toolkit::TestLib, as
    # it's used in other tests too.

    my $dbh = $wiki->store->dbh;
    my $content_sth = $dbh->prepare("INSERT INTO content
                                    (node_id,version,text,modified)
                                    VALUES (?,?,?,?)");
    my $node_sth = $dbh->prepare("INSERT INTO node
                                 (id,name,version,text,modified)
                                 VALUES (?,?,?,?,?)");

    $node_sth->execute( 10, "Ae Pub", 2, "foo", get_timestamp( days => 2 ) )
        or die $dbh->errstr;
    $content_sth->execute( 10, 2, "foo", get_timestamp( days => 2 ) )
        or die $dbh->errstr;
    $content_sth->execute( 10, 1, "foo", get_timestamp( days => 8 ) )
        or die $dbh->errstr;

    # Now write it as per usual, to get the categories in.
    my %data = $wiki->retrieve_node( "Ae Pub" );
    $wiki->write_node( "Ae Pub", $data{content}, $data{checksum},
                       { category => [ "Pubs" ] } )
      or die "Couldn't write Ae Pub node";

    # Now do Ae Bar the same way.
    $node_sth->execute( 20, "Ae Bar", 1, "foo", get_timestamp( days => 5 ) )
        or die $dbh->errstr;
    $content_sth->execute( 20, 1, "foo", get_timestamp( days => 5 ) )
        or die $dbh->errstr;
    %data = $wiki->retrieve_node( "Ae Bar" );
    $wiki->write_node( "Ae Bar", $data{content}, $data{checksum},
                       { category => [ "Bars" ] } )
      or die "Couldn't write Ae Bar node";

    # The other nodes are simple.
    $wiki->write_node( "Ae Restaurant", "lalalalala", undef,
                       { category => [ "Restaurants" ] } )
      or die "Couldn't write Ae Restaurant node";
    $wiki->write_node( "Ae Nother Pub", "beer", undef,
                       { category => [ "Pubs" ] } )
      or die "Couldn't write Ae Nother Pub node";
}