#!/usr/bin/perl

use strict;
use warnings;

use inc::testplan(1,
  + 4   # use
  + 199
  + 7   # callbacks
  + 196 # fin
);

########################################################################
BEGIN {
  use_ok('dtRdr::Annotation::IO');
  use_ok('dtRdr::Annotation::Sync::Standard');
  use_ok('dtRdr::Config');
  use_ok('dtRdr::BookBag');
}

########################################################################
# sync only these book id's
my @book_list = qw(
  xEF22EA3CDB3611DBA417B36A7806B258
  xF0D8058ADB3911DB8F2B386E7806B258
);
my @book_sync = ($book_list[0]); # but only sync one
########################################################################

use test_lib::server;

use test_inc::tempdir;
my $s_storage_dir = wants 't/_sync/s_temp';
my $l_storage_dir = wants 't/_sync/l_temp';

use test_inc::anno_copy;
{
  my %books_ok = map({$_ => 1} @book_list);
  anno_copy(
    'test_data/annotations.server.01/',
    $s_storage_dir,
    sub {$_->{public} and $books_ok{$_->{book}}},
  );
  # populate the local io with everything too
  anno_copy(
    'test_data/annotations.server.01/',
    $l_storage_dir,
    sub {$_->{public} and $books_ok{$_->{book}}},
  );
}
########################################################################

########################################################################
# setup the server *** caution, forks! ***
srand;
my $user = 'bob';
my $pass = join('', map({chr(97 + int(rand(25)))} 0..12));

my $url = test_lib::server->new(
  storage => $s_storage_dir,
  users   => {$user => $pass},
  auth_type => $ENV{DOT_SERVER_AUTH} || 'Basic',
  verbose => $ENV{DOT_SERVER_VERBOSE} || 0,
  #auth_required => 1,
  #no_auth => 1, # XXX not really working?
)->started_ok or die 'no server';
$url .= '/';
########################################################################


my $io = dtRdr::Annotation::IO->new(uri => $l_storage_dir);
# uh, pretend we already sync'd once
foreach my $item ($io->items) {
  $item->{public}{owner} = undef if($item->{public}{owner} eq $user);
  $item->{public}{server} = 'the server';
  $item->{public}{rev} = $item->{revision};
  $io->x_update($item->{id}, $item);
}

my $server = dtRdr::ConfigData::Server->new(
  id       => 'the server',
  username => $user,
  password => $pass,
  uri      => $url,
);

########################################################################
my $run_sync = sub {
  my (%opts) = @_;
  my $sync = dtRdr::Annotation::Sync::Standard->new(
    anno_io => $io,
    server  => $server,
    books   => [@book_sync],
    %opts
  );

  # run it
  $sync->start;

  my $counter = 0;
  until($sync->done) {
    $sync->work;
    ($counter++ > 1000) and last; # just to have some limit
  }
  ok($sync->done, 'done') or die "latency trouble?";
}; # end sub $run_sync
########################################################################
my $get_yml = sub {
  my ($id) = @_;
  return YAML::Syck::LoadFile(
    'test_data/annotations.input.01/' . $id .'.yml');
};
use test_inc::anno_io_verify;
my $verify = sub {
  anno_io_verify($s_storage_dir, $io, $server, \@book_list);
};

$run_sync->();
$verify->();

use dtRdr::Book;
sub zombie_anno {
  my ($h, $io) = @_;
  my $book = dtRdr::Book::Zombie->new(id => $h->{book});
  my $type = $h->{type};
  # load if needed
  my $anno_type = dtRdr::Annotation::IO->_anno_type($type);
  my $anno = $type->deserialize($h, book => $book);
  if($io) {
    my $method = 'add_' . $anno_type;
    $book->$method($anno);
    $book->set_anno_io($io);
  }
  return($anno);
} # end subroutine zombie_anno
########################################################################

# to deref
my $OBlob = sub {dtRdr::Annotation::IOBlob->outgoing(%{$_[0]});};

# do some deletes and mods from a different UA
# TODO maybe also from a different user
my $bm_mod = 'x0DA4CBC4C15111DB884BD901C9B462D6';
{ # could create a completely new io but probably just do some puts
  {
    package MyUA;
    use base 'LWP::UserAgent';
    sub get_basic_credentials {return($user, $pass);}
    # GRR, why can't I have delete and put methods?
    sub req {
      my $self = shift;
      my $ans = $self->request(HTTP::Request->new(@_));
      my %want = (
        GET    => 200,
        POST   => 201,
        DELETE => 200,
        PUT    => 200,
      );
      ($ans->code == $want{$_[0]}) or
        die "bad answer ", $ans->code, " ", $ans->content;
      my $cont = $ans->content;
      return($cont);
    }
  }
  my $ua = MyUA->new();
  # OOPS, the other client needs to login when we're doing cookies
  {
    if(my $ans = $ua->req('GET', $url.'config.yml')) {
      my $data = YAML::Syck::Load($ans);
      if(my $lconf = $data->{login}) {
        require HTTP::Cookies;
        my $cookies = HTTP::Cookies->new;
        $ua->cookie_jar($cookies);
        my $template = $lconf->{template} or die "need template for login";
        $template =~ s/#USERNAME#/$user/ or die "no #USERNAME# in template?";
        $template =~ s/#PASSWORD#/$pass/ or die "no #PASSWORD# in template?";
        my $ans = $ua->request(HTTP::Request->new(
          'POST', $lconf->{url}, [], $template
        ));
      }
    }
  }
  my $bm = $OBlob->(grep({$_->{id} eq $bm_mod} $io->items));
  $bm or die "drat";
  $bm->set_title('Test Coverage Rocks');
  $bm->set_revision(1);

  $ua->req('PUT', $url . "annotation/$bm_mod.yml?rev=0",
    [content_type => 'text/x-yaml'],
    YAML::Syck::Dump($bm->deref)
  );

} # ugh, deleted, putted, posted
{ # setup book callbacks pointed at counters
  my $a_book = dtRdr::Book::Zombie->new(id => $book_sync[0]);
  $io->apply_to($a_book);
  my %hits;
  my %changed;
  foreach my $event (qw(created changed deleted)) {
    $hits{$event} = 0;
    my $setter = 'set_annotation_' . $event . '_sub';
    dtRdr::Book->callback->$setter(sub {
      my $anno = shift;
      $hits{$event}++;
      $changed{$anno->id} = $event;
    });
  }
  my $bag = dtRdr::BookBag->new(books => [$a_book]);
  $run_sync->(bookbag => $bag);
  $verify->();
  is($hits{deleted}, 0, 'count deleted');
  is($hits{changed}, 1, 'count changed');
  is($hits{created}, 0, 'count created');
  is_deeply(\%changed,
  {
    $bm_mod => 'changed',
  }, 'callbacks are golden');
  {
    my $anno = $a_book->find_bookmark($bm_mod);
    is($anno->title, 'Test Coverage Rocks');
  }
}

done;
# vim:ts=2:sw=2:et:sta:syntax=perl