The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::SD::Replica::hm;
use Any::Moose;
extends 'App::SD::ForeignReplica';
use Params::Validate qw(:all);
use UNIVERSAL::require;
use URI;
use Memoize;
use Prophet::ChangeSet;
use File::Temp 'tempdir';

has hm               => ( isa => 'Net::Jifty', is => 'rw' );
has remote_url       => ( isa => 'Str',        is => 'rw' );
has foreign_username => ( isa => 'Str',        is => 'rw' );
has props            => ( isa => 'HashRef',    is => 'rw' );

use constant scheme       => 'hm';
use constant pull_encoder => 'App::SD::Replica::hm::PullEncoder';
use constant push_encoder => 'App::SD::Replica::hm::PushEncoder';

# XXX TODO - kill the query requirement by refactoring sub run in the superclass
use constant query => '';

=head2 BUILD

Open a connection to the source identified by C<$self->{url}>.

=cut

# XXX: this should be called from superclass, or better, have individual attributes have their own builders.

sub BUILD {
    my $self = shift;
    require Net::Jifty;
    my ( $server, $props ) = $self->{url} =~ m/^hm:(.*?)(?:\|(.*))?$/
        or die
        "Can't parse Hiveminder server spec. Expected hm:http://hiveminder.com or hm:http://hiveminder.com|props";
    $self->url($server);
    my $uri = URI->new($server);
    my ( $username, $password );
    if ( $uri->can('userinfo') && ( my $auth = $uri->userinfo ) ) {
        ( $username, $password ) = split /:/, $auth, 2;
        $uri->userinfo(undef);
    }
    $self->remote_url("$uri");
    $self->foreign_username($username) if ($username);

    ( $username, $password )
        = $self->prompt_for_login(
            uri      => $uri,
            username => $username,
        ) unless $password;

    if ($props) {
        my %props = split /=|;/, $props;
        $self->props( \%props );
    }
    $self->hm(
        Net::Jifty->new(
            site        => $self->remote_url,
            cookie_name => 'JIFTY_SID_HIVEMINDER',

            email    => $username,
            password => $password
        )
    );
}

=head2 uuid

Return the replica's UUID

=cut

sub uuid {
    my $self = shift;
    return $self->uuid_for_url( join( '/', $self->remote_url, $self->foreign_username ) );
}

sub get_txn_list_by_date {
    my $self   = shift;
    my $ticket = shift;
    my @txns   = map {
        my $txn_created_dt = App::SD::Util::string_to_datetime( $_->{modified_at} );
        unless ($txn_created_dt) {
            die "Couldn't parse '" . $_->{modified_at} . "' as a timestamp";
        }
        my $txn_created = $txn_created_dt->epoch;

        return { id => $_->{id}, creator => $_->{creator}, created => $txn_created }
        }

        sort { $a->{'id'} <=> $b->{'id'} }
        @{ $self->hm->search( 'TaskTransaction', task_id => $ticket ) || [] };

    return @txns;
}

sub user_info {
    my $self = shift;
    my %args = @_;
    return $self->_user_info( keys %args ? (%args) : ( email => $self->foreign_username ) );
}

sub _user_info {
    my $self   = shift;
    my $key = shift;
    my $value = shift;
    return undef unless defined $value;
    my $status = $self->hm->search('User', $key => $value);
    die $status->{'error'} unless $status->[0]->{'id'};
    return $status->[0];
}
memoize '_user_info';

sub remote_uri_path_for_id {
    my $self = shift;
    my $id   = shift;
    return "/task/" . $id;
}

our %PROP_MAP = (
    owner_id                 => 'owner',
    requestor_id             => 'reporter',
    priority                 => 'priority_integer',
    completed_at             => 'completed',
    due                      => 'due',
    creator                  => 'creator',
    milestone                => '_delete',
    attachment_count         => '_delete',
    depended_on_by_count     => '_delete',
    depended_on_by_summaries => '_delete',
    depends_on_count         => '_delete',
    depends_on_summaries     => '_delete',
    group_id                 => '_delete',
    last_repeat              => '_delete',
    repeat_days_before_due   => '_delete',
    repeat_every             => '_delete',
    repeat_of                => '_delete',
    repeat_next_create       => '_delete',
    repeat_period            => '_delete',
    repeat_stacking          => '_delete',

);

our %REV_PROP_MAP = ();
while ( my ( $k, $v ) = each %PROP_MAP ) {
    if ( $REV_PROP_MAP{$v} ) {
        $REV_PROP_MAP{$v} = [ $REV_PROP_MAP{$v} ]
            unless ref $REV_PROP_MAP{$v};
        push @{ $REV_PROP_MAP{$v} }, $k;
    } else {
        $REV_PROP_MAP{$v} = $k;
    }
}

sub property_map {
    my $self = shift;
    my $dir = shift || 'pull';
    if ( $dir eq 'pull' ) {
        return %PROP_MAP;
    } elsif ( $dir eq 'push' ) {
        return %REV_PROP_MAP;
    } else {
        die "unknown direction";
    }
}

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