package App::bif::show::timesheet;
use strict;
use warnings;
use utf8;
use Bif::Mo;
use DBIx::ThinSQL qw/ sq /;
use Time::Piece;
use Time::Seconds;

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

sub _build {
    my $count = @_;

    my @columns;
    my @select;
    my $format = ' l  l  ';
    my @headers;

    my $i = 1;
    foreach my $item (@_) {
        my ( $type, $start, $delta, $rollup ) = @{$item};
        my $end;

        if ( $type eq 'year' ) {
            $start = Time::Piece->strptime( $start->strftime('%Y'), '%Y' );
            $start = $start->add_years($delta);
            $end   = $start->add_years(1);
        }
        elsif ( $type eq 'month' ) {
            $start =
              Time::Piece->strptime( $start->strftime('%Y-%m'), '%Y-%m' );
            $start = $start->add_months($delta);
            $end   = $start->add_months(1);
        }
        elsif ( $type eq 'week' ) {
            $start =
              Time::Piece->strptime( $start->strftime('%Y-%m-%d'), '%Y-%m-%d' );
            my $wday = $start->_wday - 1;
            $wday = 6 if $wday < 0;
            $start = $start + ( -$wday * ONE_DAY );
            $start = $start + ( $delta * 7 * ONE_DAY );
            $end   = $start + ( 7 * ONE_DAY );
        }
        elsif ( $type eq 'day' ) {
            $start =
              Time::Piece->strptime( $start->strftime('%Y-%m-%d'), '%Y-%m-%d' );
            $start = $start + ( $delta * ONE_DAY );
            $end   = $start + (ONE_DAY);
        }
        else {
            die "unknown type: $type";
        }

        push( @columns,
                " printf('%d:%0.2d', "
              . " SUM(col$i) / 3600, "
              . " (SUM(col$i) - 3600 * (SUM(col$i) / 3600)) / 60 "
              . " ) AS col$i" );

        my @x;
        foreach my $j ( 1 .. $count ) {
            if ( $i == $j ) {
                push( @x, "wd.delta AS col$j" );
            }
            else {
                push( @x, "NULL AS col$j" );
            }
        }

        push(
            @select,
            [
                ( $i == 1 ? 'select' : 'union_all_select' ) =>
                  [ 't.kind AS kind', 'p.path AS path', @x, ],
                from                   => 'i',
                inner_join             => 'changes c',
                "on -- $start -> $end" => {
                    'i.identity_id' => \'i.identity_id',
                    'c.mtime >='    => $start->epoch,
                    'c.mtime <'     => $end->epoch,
                },
                inner_join => 'work_deltas wd',
                on         => 'wd.change_id = c.id',
                $rollup
                ? (
                    inner_join => 'projects_tree pt',
                    on         => 'pt.child = wd.topic_id',
                    inner_join => 'projects p',
                    on         => 'p.id = pt.parent',
                  )
                : (
                    inner_join => 'projects p',
                    on         => 'p.id = wd.topic_id',
                ),
                inner_join => 'topics t',
                on         => 't.id = p.id',
            ]
        );

        $format .= 'r  ';

        if ( $type eq 'year' ) {
            push( @headers, $start->strftime('%Y') );
        }
        elsif ( $type eq 'month' ) {
            push( @headers, $start->strftime('%Y-%m') );
        }
        elsif ( $type eq 'week' ) {

            # Time::Piece's %V does not work under windows, so use the
            # ->method() calls instead. See
            # https://rt.cpan.org/Public/Bug/Display.html?id=105507
            push( @headers,
                sprintf( '%0.4d-W%0.2d', $start->year, $start->week ) );
        }
        elsif ( $type eq 'day' ) {
            push( @headers, $start->strftime('%Y-%m-%d') );
        }

        $i++;
    }

    $format .= ' ';

    return \@columns, \@select, $format, \@headers;
}

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

    $opts->{date} //= localtime->strftime('%Y-%m-%d');
    my $dt = eval { Time::Piece->strptime( $opts->{date}, '%Y-%m-%d' ) }
      or return $self->err( 'InvalidDate', 'invalid date string: %s',
        $opts->{date} );

    my ( $col, $select, $format, $header );

    if ( $opts->{year} ) {
        $opts->{number} ||= 4;
        ( $col, $select, $format, $header ) =
          _build(
            map { [ 'year', $dt, -( $opts->{number} - $_ ), $opts->{rollup} ] }
              1 .. $opts->{number} );
    }
    elsif ( $opts->{month} ) {
        $opts->{number} ||= 4;
        ( $col, $select, $format, $header ) = _build(
            map { [ 'month', $dt, -( $opts->{number} - $_ ), $opts->{rollup} ] }
              1 .. $opts->{number}
        );
    }
    elsif ( $opts->{week} ) {
        $opts->{number} ||= 4;
        ( $col, $select, $format, $header ) =
          _build(
            map { [ 'week', $dt, -( $opts->{number} - $_ ), $opts->{rollup} ] }
              1 .. $opts->{number} );
    }
    elsif ( $opts->{day} ) {
        $opts->{number} ||= 3;
        ( $col, $select, $format, $header ) =
          _build(
            map { [ 'day', $dt, -( $opts->{number} - $_ ), $opts->{rollup} ] }
              1 .. $opts->{number} );
    }
    else {
        ( $col, $select, $format, $header ) = _build(
            [ 'day',   $dt, 0, $opts->{rollup} ],
            [ 'week',  $dt, 0, $opts->{rollup} ],
            [ 'month', $dt, 0, $opts->{rollup} ],
            [ 'year',  $dt, 0, $opts->{rollup} ],
        );
    }

    my @data = $db->xarrayrefs(
        with => 'i',
        as   => sq(
            select => 'b.identity_id AS identity_id',
            from   => 'bifkv b',
            where  => { key => 'self' },
        ),
        select   => [ 'kind', 'path', @$col, ],
        from     => sq( map   { @$_ } @$select ),
        group_by => [qw/kind path/],
        order_by => 'path',
    );

    if ( !@data ) {
        print "Timesheet is empty for selected period.\n";
    }
    else {
        $self->start_pager;
        print $self->render_table( $format, [ qw/Type Path/, @$header ],
            \@data );
    }

    if ( $opts->{year} ) {
        return $self->ok('ShowTimesheetYear');
    }
    elsif ( $opts->{month} ) {
        return $self->ok('ShowTimesheetMonth');
    }
    elsif ( $opts->{week} ) {
        return $self->ok('ShowTimesheetWeek');
    }
    elsif ( $opts->{day} ) {
        return $self->ok('ShowTimesheetDay');
    }

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

1;
__END__

=head1 NAME

=for bif-doc #show

bif-show-timesheet - display time worked per project

=head1 VERSION

0.1.5_3 (2015-06-30)

=head1 SYNOPSIS

    bif show timesheet [DATE] [OPTIONS...]

=head1 DESCRIPTION

The C<bif-show-timesheet> command displays the amount of work recorded
on projects over the current year, month, week, and day.

=head1 ARGUMENTS & OPTIONS

=over

=item DATE

A date in format YYYY-MM-DD from which the first time period is
selected. Defaults to the current day.

=item --day, -d

Display multiple daily timesheets. The default number is 3.

=item --month, -m

Display multiple monthly timesheets. The default number is 4.

=item --number, -n NUMBER

Display NUMBER time periods instead of the default.

=item --rollup, -r

Roll times worked into the parent project(s) for accumulative values.

=item --week, -w

Display multiple weekly timesheets. The default number is 4.

=item --year, -y

Display multiple yearly timesheets. The default number is 4.

=back

=head1 SEE ALSO

L<bif>(1)

=head1 AUTHOR

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

=head1 COPYRIGHT AND LICENSE

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

