The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::SD::ForeignReplica::PullEncoder;
use Any::Moose;
use App::SD::Util;
use Params::Validate qw/validate/;

sub run {
    my $self = shift;
    my %args = validate( @_, { after => 1});

    $self->sync_source->log('Finding matching tickets');
    my $tickets = $self->find_matching_tickets( query => $self->sync_source->query );

    if ( @$tickets == 0 ) {
        $self->sync_source->log("No tickets found.");
        return;
    }

    my $counter = 0;
    $self->sync_source->log_debug("Discovering ticket history");

    my ( $last_modified, $last_txn, @changesets );

    my $progress = Time::Progress->new();
    $progress->attr( max => $#$tickets );

    for my $ticket (@$tickets) {
        $counter++;
        my $changesets;
        print $progress->report( "Fetching ticket history %30b %p Est: %E\r", $counter );
        $self->sync_source->log_debug( "Fetching $counter of " . scalar @$tickets  . " tickets");
        ( $last_modified, $changesets ) = $self->transcode_ticket( $ticket, $last_modified );
        unshift @changesets, @$changesets;
    }
    return [ sort { App::SD::Util::string_to_datetime($a->created) <=> App::SD::Util::string_to_datetime( $b->created) } @changesets];
}

sub ticket_last_modified { undef}

sub transcode_ticket {
    my $self          = shift;
    my $ticket        = shift;
    my $last_modified = shift;
    my @changesets;

    if ( my $ticket_last_modified = $self->ticket_last_modified($ticket) ) {

        $last_modified = $ticket_last_modified if ( !$last_modified || $ticket_last_modified > $last_modified );
    }

    my $transactions = $self->find_matching_transactions(
        ticket               => $ticket,
        starting_transaction => $self->sync_source->app_handle->handle->last_changeset_from_source(
            $self->sync_source->uuid_for_remote_id( $self->ticket_id($ticket) )
            ) || 1
    );

    my $changesets;
    ( $last_modified, $changesets ) = $self->transcode_history( $ticket, $transactions, $last_modified );
    return ( $last_modified, $changesets );
}


sub transcode_history {
    my $self          = shift;
    my $ticket        = shift;
    my $transactions  = shift;
    my $last_modified = shift;
    my $ticket_id     = $self->ticket_id($ticket);

    my @changesets;

    # Walk transactions newest to oldest.
    my $txn_counter         = 0;
    my ($initial_state, $final_state)          = $self->translate_ticket_state($ticket, $transactions);


    for my $txn ( sort { $b->{'serial'} <=> $a->{'serial'} } @$transactions ) {
        $last_modified = $txn->{timestamp} if ( !$last_modified || ( $txn->{timestamp} > $last_modified ) );
        $self->sync_source->log_debug( "$ticket_id Transcoding transaction " . ++$txn_counter . " of " . scalar @$transactions );

        my $changeset = $self->transcode_one_txn( $txn, $initial_state, $final_state );
        next unless $changeset && $changeset->has_changes;

        # the changesets are older than the ones that came before, so they goes first
        unshift @changesets, $changeset;
    }
    return ( $last_modified, \@changesets );
}


sub translate_ticket_state {
    die 'translate_ticket_state must be mplemented';
}

sub warp_list_to_old_value {
    my $self    = shift;
    my $current = shift;
    my $add     = shift;
    my $del     = shift;
    $_ = '' foreach grep !defined, $current, $add, $del;

    my @new = grep defined && length, split /\s*,\s*/, $current;
    my @old = grep defined && length && $_ ne $add, (@new, $del);
    return join( ", ", @old );
}

=head2 _only_pull_tickets_modified_after

If we've previously pulled from this sync source, this routine will
return a datetime object. It's safe not to evaluate any ticket last
modified before that datetime

=cut

sub _only_pull_tickets_modified_after {
    my $self = shift;

    # last modified date is in GMT and searches are in user-time XXX -check assumption
    # because of this, we really want to back that date down by one day to catch overlap
    # XXX TODO we are playing FAST AND LOOSE WITH DATE MATH
    # XXX TODO THIS WILL HURT US SOME DAY
    # At that time, Jesse will buy you a beer.
    my $last_pull = $self->sync_source->upstream_last_modified_date();
    return undef unless $last_pull;
    my $before = App::SD::Util::string_to_datetime($last_pull);
    die "Failed to parse '" . $self->sync_source->upstream_last_modified_date() . "' as a timestamp"
        unless ($before);

    # 26 hours ago deals with most any possible timezone/dst edge case
    $before->subtract( hours => 26 );

    return $before;
}

sub new_comment_creation_change {
	my $self = shift;
	return Prophet::Change->new(
        {
            record_type => 'comment',
            record_uuid =>  $self->sync_source->uuid_generator->create_str()
            ,    # comments are never edited, we can have a random uuid
            change_type => 'add_file'
        }
    );
}

__PACKAGE__->meta->make_immutable;
no Any::Moose;
1;