package App::bif::pull::project;
use strict;
use warnings;
use Bif::Mo;
use AnyEvent;
use Bif::Sync::Client;
use Coro;
use DBIx::ThinSQL qw/qv sq/;

our $VERSION = '0.1.5_5';
extends 'App::bif';

sub run {
    my $self  = shift;
    my $opts  = $self->opts;
    my $dbw   = $self->dbw;
    my $pinfo = $self->get_project( $opts->{path} );

    $opts->{hub} //= $dbw->xval(
        select     => 'p.default_hub_id',
        from       => 'nodes_tree nt',
        inner_join => 'projects p',
        on         => 'p.id = nt.parent AND p.default_hub_id IS NOT NULL',
        where      => { 'nt.child' => $pinfo->{id} },
        order_by   => 'nt.depth DESC',
        limit      => 1,
    );

    return $self->err( 'ProjectNoHub', 'project hub not known' )
      unless $opts->{hub};

    my $hinfo = $self->get_hub( $opts->{hub} );

    warn "warning: already imported: $pinfo->{path}\n"
      if $pinfo->{local};

    $|++;    # no buffering
    my $error;
    my $cv = AE::cv;

    my $client = Bif::Sync::Client->new(
        name          => $hinfo->{name},
        db            => $dbw,
        location      => $hinfo->{location},
        debug         => $opts->{debug},
        debug_bifsync => $opts->{debug_bifsync},
        on_update     => sub {
            my $client = shift;
            $self->lprint( $client->name . ': ' . $_[0] );
        },
        on_error => sub {
            $error = shift;
            $cv->send;
        },
    );

    my $fh   = select;
    my $coro = async {
        select $fh;

        eval {
            $dbw->txn(
                sub {
                    $self->start_work(
                        node_id       => $pinfo->{id},
                        start         => time,
                        start_comment => "pull project",
                        billable      => 1,
                        save          => 1,
                    );

                    $dbw->xdo(
                        update => 'projects',
                        set    => 'local = 1',
                        where  => { id => $pinfo->{id} },
                    );

                    # Set the default hub here (if this project
                    # is already a hub-level project)
                    $dbw->xdo(
                        update => 'projects',
                        set    => { default_hub_id => $hinfo->{id} },
                        where  => {
                            'default_hub_id !' => undef,
                            id                 => $pinfo->{id}
                        },
                    );

                    $self->stop_work(
                        stop    => time,
                        restore => 1,
                    );

                    my $status = $client->sync_hub( $hinfo->{id} );

                    unless ( $status eq 'HubMatch'
                        or $status eq 'HubSync' )
                    {
                        $dbw->rollback;
                        $error =
" unexpected HubSync/Match status received : $status ";
                        return;

                    }

                    if ( $status eq 'HubSync' ) {
                        $status = $client->transfer_hub_changes;

                        if ( $status ne 'TransferHubChanges' ) {
                            $dbw->rollback;
                            $error =
" unexpected TransferHubChanges status received : $status ";
                            return;
                        }
                    }

                    print "\n";
                    $self->lprint('');
                    $client->name("$hinfo->{name}\[$pinfo->{path}\]");

                    $status = $client->sync_project( $pinfo->{id} );

                    unless ( $status eq 'ProjectSync'
                        or $status eq 'ProjectMatch' )
                    {
                        $dbw->rollback;
                        $error =
" unexpected ProjectSync/Match status received : $status ";
                        return;
                    }

                    if ( $status eq 'ProjectSync' ) {
                        $status = $client->transfer_project_related_changes;

                        if ( $status ne 'TransferProjectRelatedChanges' ) {
                            $dbw->rollback;
                            $error =
" unexpected TransferProjectRelatedChanges status received : $status ";
                            return;
                        }
                    }

                    $dbw->xdo(
                        insert_or_replace_into =>
                          [ 'bifkv', qw/key change_id change_id2/ ],
                        select =>
                          [ qv('last_sync'), 'MAX(c.id)', 'MAX(c.id)', ],
                        from => 'changes c',
                    );

                    print "\n";
                    return;
                }
            );
        };

        if ($@) {
            $error = $@;
            print " \n ";
        }

        return $cv->send;
    };

    $cv->recv;
    $client->disconnect;
    return $self->err( 'Unknown', $error ) if $error;
    $dbw->do('ANALYZE');
    return $self->ok('PullProject');

}

1;
__END__

=head1 NAME

=for bif-doc #sync

bif-pull-project -  import projects from a remote hub

=head1 VERSION

0.1.5_5 (2015-08-13)

=head1 SYNOPSIS

    bif pull project PATH

=head1 DESCRIPTION

The L<bif-pull-project> command imports remote projects from a hub. If
a project has been imported, then it is considered "local". If it has
not been imported then we call it "remote".

For example:

=begin bif

    bif init
    bif pull hub org@provider

=end bif

    bif list projects
    bif pull todo

=head1 ARGUMENTS

=over

=item PATH

The path of the remote project to import.

=item --hub HUB

If the project exists at multiple hubs the C<--hub> option can be used
to force which hub to pull from.

=back

=head1 SEE ALSO

L<bif>(1)

=head1 AUTHOR

Mark Lawrence E<lt>nomad@null.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2013-2015 Mark Lawrence <nomad@null.net>

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

