package App::bif::new::project;
use strict;
use warnings;
use Bif::Mo;
use IO::Prompt::Tiny qw/prompt/;
use DBIx::ThinSQL qw/ qv sq/;

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

sub dup {
    my $self = shift;
    my $opts = $self->opts;
    my $dbw  = $self->dbw;

    my $path      = $opts->{path};
    my $dup_pinfo = $self->get_project( $opts->{dup} );

    $opts->{title} ||= $dbw->xval(
        select => 'p.title',
        from   => 'projects p',
        where  => { 'p.id' => $dup_pinfo->{id} },
    );

    my $src = $dbw->xval(
        select    => 'p.path',
        from      => 'projects p',
        left_join => 'hubs h',
        on        => 'h.id = p.hub_id',
        where     => { 'p.id' => $dup_pinfo->{id} },
    );

    $opts->{message} ||=
      $self->prompt_edit( txt => "[ dup: $src ]\n", opts => $self );

    $dbw->txn(
        sub {
            $opts->{id} ||= $dbw->nextval('topics');
            $opts->{change_id} ||= $self->new_change;

            $dbw->xdo(
                insert_into => 'func_new_project',
                values      => {
                    change_id => $opts->{change_id},
                    id        => $opts->{id},
                    parent_id => $opts->{parent_id},
                    name      => $opts->{name},
                    title     => $opts->{title},
                },
            );

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

            # TODO: unecessary?
            if ( $dup_pinfo->{hub_id} ) {
                $dbw->xdo(
                    insert_into => 'func_update_project',
                    values      => {
                        id        => $opts->{id},
                        change_id => $opts->{change_id},
                        hub_id    => $dup_pinfo->{hub_id},
                    },
                );
            }

            my @status = $dbw->xhashrefs(
                select    => [ 'ps.status', 'ps.rank', 'p.id AS current_id' ],
                from      => 'project_status ps',
                left_join => 'projects p',
                on       => 'p.project_status_id = ps.id',
                where    => { 'ps.project_id' => $dup_pinfo->{id} },
                order_by => 'ps.rank',
            );

            my $status_id;
            foreach my $status (@status) {
                my $sid = $dbw->nextval('topics');
                $status_id = $sid if $status->{current_id};

                $dbw->xdo(
                    insert_into => 'func_new_project_status',
                    values      => {
                        change_id  => $opts->{change_id},
                        id         => $sid,
                        project_id => $opts->{id},
                        status     => $status->{status},
                        rank       => $status->{rank},
                    }
                );
            }

            $dbw->xdo(
                insert_into => 'func_update_project',
                values      => {
                    id                => $opts->{id},
                    change_id         => $opts->{change_id},
                    project_status_id => $status_id,
                },
            );

            @status = $dbw->xhashrefs(
                select => [ 'ist.status', 'ist.rank', 'ist.def' ],
                from   => 'issue_status ist',
                where    => { 'ist.project_id' => $dup_pinfo->{id} },
                order_by => 'ist.rank',
            );

            foreach my $status (@status) {
                my $sid = $dbw->nextval('topics');

                $dbw->xdo(
                    insert_into => 'func_new_issue_status',
                    values      => {
                        change_id  => $opts->{change_id},
                        id         => $sid,
                        project_id => $opts->{id},
                        status     => $status->{status},
                        rank       => $status->{rank},
                        def        => $status->{def},
                    }
                );
            }

            @status = $dbw->xhashrefs(
                select => [ 'ts.status', 'ts.rank', 'ts.def' ],
                from   => 'task_status ts',
                where    => { 'ts.project_id' => $dup_pinfo->{id} },
                order_by => 'ts.rank',
            );

            foreach my $status (@status) {
                my $sid = $dbw->nextval('topics');

                $dbw->xdo(
                    insert_into => 'func_new_task_status',
                    values      => {
                        change_id  => $opts->{change_id},
                        id         => $sid,
                        project_id => $opts->{id},
                        status     => $status->{status},
                        rank       => $status->{rank},
                        def        => $status->{def},
                    }
                );
            }

            my $path = $dbw->xval(
                select => 'path',
                from   => 'topic_path tp',
                where  => { 'tp.id' => $opts->{id} },
            );

            $self->end_change(
                id => $opts->{change_id},
                action_format =>
                  "new project $path (%s) --dup $dup_pinfo->{path} (%s)]",
                action_topic_id_1 => $opts->{id},
                action_topic_id_1 => $dup_pinfo->{id},
                message           => $opts->{message},
            );

        }
    );

    return $self->ok('NewProject');
}

sub new_project {
    my $self = shift;
    my $kind = shift || 'project';
    my $dbw  = $self->dbw;
    my $opts = $self->opts;

    $dbw->xdo(
        insert_into => 'func_new_project',
        values      => {
            change_id => $opts->{change_id},
            id        => $opts->{id},
            parent_id => $opts->{parent_id},
            name      => $opts->{name},
            title     => $opts->{title},
        },
    );

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

    my @status = $dbw->xhashrefs(
        select   => [ qw/status rank/, ],
        from     => 'default_status',
        where    => { kind => $kind },
        order_by => 'rank',
    );

    foreach my $status (@status) {
        my $sid = $dbw->nextval('topics');

        $dbw->xdo(
            insert_into => 'func_new_project_status',
            values      => {
                change_id  => $opts->{change_id},
                id         => $sid,
                project_id => $opts->{id},
                status     => $status->{status},
                rank       => $status->{rank},
            }
        );
    }

    $dbw->xdo(
        insert_into =>
          [ 'func_update_project', 'id', 'change_id', 'project_status_id', ],
        select =>
          [ qv( $opts->{id} ), qv( $opts->{change_id} ), 'project_status.id', ],
        from       => 'default_status',
        inner_join => 'project_status',
        on         => {
            project_id              => $opts->{id},
            'default_status.status' => \'project_status.status',
        },
        where => do {

            if ( $opts->{status} ) {
                {
                    'default_status.kind'   => 'project',
                    'default_status.status' => $opts->{status},
                };
            }
            else {
                {
                    'default_status.kind' => 'project',
                    'default_status.def'  => 1,
                };
            }
        },
    );

    @status = $dbw->xhashrefs(
        select   => [ qw/status rank def/, ],
        from     => 'default_status',
        where    => { kind => 'issue' },
        order_by => 'rank',
    );

    foreach my $status (@status) {
        my $sid = $dbw->nextval('topics');
        $dbw->xdo(
            insert_into => 'func_new_issue_status',
            values      => {
                change_id  => $opts->{change_id},
                id         => $sid,
                project_id => $opts->{id},
                status     => $status->{status},
                rank       => $status->{rank},
                def        => $status->{def},
            }
        );
    }

    @status = $dbw->xhashrefs(
        select   => [ qw/status rank def/, ],
        from     => 'default_status',
        where    => { kind => 'task' },
        order_by => 'rank',
    );

    foreach my $status (@status) {
        my $sid = $dbw->nextval('topics');
        $dbw->xdo(
            insert_into => 'func_new_task_status',
            values      => {
                change_id  => $opts->{change_id},
                id         => $sid,
                project_id => $opts->{id},
                status     => $status->{status},
                rank       => $status->{rank},
                def        => $status->{def},
            }
        );
    }

}

sub run {
    my $self = shift;
    my $opts = $self->opts;
    my $dbw  = $self->dbw;

    $opts->{path} ||= prompt( 'Path:', '' )
      || return $self->err( 'ProjectPathRequired', 'project path is required' );

    my @parts = split( '/', $opts->{path} );
    $opts->{name} = pop @parts;

    my $parent = join( '/', @parts );
    my @parents = $dbw->get_projects($parent);

    if ( @parents > 1 ) {
        return $self->err( 'ParentProjectAmbiguous',
            "parent path is ambiguous:\n    "
              . join( "\n    ", map { $_->{path} } @parents ) );
    }
    elsif (@parents) {
        $opts->{parent_id} = $parents[0]->{id};
        @parts = split( '/', $parents[0]->{path} );
    }
    elsif (@parts) {
        return $self->err( 'HubNotFound',
            "hub or parent project not found: " . join( '/', @parts ) );
    }
    else {
        unshift( @parts, 'local' );
    }

    $opts->{path} = join( '/', @parts, $opts->{name} );
    my @x = $dbw->get_projects( $opts->{path} );
    return $self->err( 'ProjectExists', "project exists: $opts->{path}" )
      if @x;

    my $where;
    if ( $opts->{status} ) {
        return $self->err( 'InvalidStatus',
            'unknown status: ' . $opts->{status} )
          unless $dbw->xarrayref(
            select => 'count(*)',
            from   => 'default_status',
            where  => {
                kind   => 'project',
                status => $opts->{status},
            }
          );
    }

    return dup($self) if $opts->{dup};

    $opts->{title} ||= prompt( 'Title:', '' )
      || return $self->err( 'ProjectTitleequired',
        'project title is required' );

    $opts->{message} ||= $self->prompt_edit( opts => $self );
    $opts->{lang} ||= 'en';

    $dbw->txn(
        sub {
            $opts->{id} ||= $dbw->nextval('topics');
            $opts->{change_id} ||= $self->new_change;

            $self->new_project;

            my $path = $dbw->xval(
                select => 'path',
                from   => 'topic_path tp',
                where  => { 'tp.id' => $opts->{id} },
            );

            $self->end_change(
                id                => $opts->{change_id},
                message           => $opts->{message},
                action_format     => "new project $path (%s)",
                action_topic_id_1 => $opts->{id},
            );

        }
    );

    return $self->ok('NewProject');
}

1;
__END__

=head1 NAME

=for bif-doc #create

bif-new-project - create a new project

=head1 VERSION

0.1.5_3 (2015-06-30)

=head1 SYNOPSIS

    bif new project [PATH] [TITLE] [OPTIONS...]

=head1 DESCRIPTION

The B<bif-new-project> command creates a new project topic.

For example, to create a local project:

    $ bif new project myproject

Same as the above but explicit about being local:

    $ bif new project local/myproject

Create a sub-project:

    $ bif new project myproject/child

Create a project directly at a known hub:

    $ bif new project hub/myproject


=head1 ARGUMENTS & OPTIONS

=over

=item PATH

An identifier for the project that specifies its hub, parent project,
and name. The project is assumed to be "local" if the first part of the
path does not identify a known hub.  The PATH is prompted for if not
provided.

=item TITLE

A short summary of what the project is about. Will be prompted for if
not provided.

=item --dup, -d SRC

Duplicate the new project title and status types (project-status,
issue-status, task-status) from SRC, where SRC is an existing project
path. The SRC title can be overriden by providing a TITLE as the second
argument as described above.

=item --message, -m MESSAGE

The project description.  An editor will be invoked to record a MESSAGE
if this option is not used.

=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.

