The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::TimeTracker::Data::Task;
use 5.010;

# ABSTRACT: App::TimeTracker Task storage

use Moose;
use App::TimeTracker::Utils qw(now);
use namespace::autoclean;
use App::TimeTracker;
use DateTime::Format::ISO8601;
use DateTime::Format::Duration;
use User::pwent;

use MooseX::Storage;
with Storage(
    format => [ JSONpm => { json_opts => { pretty => 1, canonical => 1 } } ],
    io     => "File",
);

MooseX::Storage::Engine->add_custom_type_handler(
    'DateTime' => (
        expand   => sub { DateTime::Format::ISO8601->parse_datetime(shift) },
        collapse => sub { (shift)->iso8601 } ) );
my $dtf_dur = DateTime::Format::Duration->new( pattern => '%H:%M:%S',
    normalise => 1 );
my $dtf_sec = DateTime::Format::Duration->new( pattern => '%s' );

has 'start' => (
    isa      => 'DateTime',
    is       => 'ro',
    required => 1,
    default  => sub { now() } );
has 'stop' => (
    isa     => 'DateTime',
    is      => 'rw',
    trigger => \&_calc_duration,
);
has 'seconds' => (
    isa        => 'Maybe[Int]',
    is         => 'rw',
    lazy_build => 1,
);

sub _build_seconds {
    my $self  = shift;
    my $delta = now()->subtract_datetime( $self->start );
    my $s     = $dtf_sec->format_duration($delta);
    return undef unless $s > 1;
    return $s;
}
has 'duration' => (
    isa => 'Str',
    is  => 'rw',
);
has 'status' => (
    isa => 'Str',
    is  => 'rw',
);

sub _build_user {
    return @{ getpw($<) }[0];
}
has 'user' => (
    isa        => 'Str',
    is         => 'ro',
    required   => 1,
    lazy_build => 1,
);
has 'project' => (
    isa      => 'Str',
    is       => 'ro',
    required => 1,
);
has 'description' => (
    isa      => 'Maybe[Str]',
    is       => 'ro',
    required => 0,
);
has 'tags' => (
    isa     => 'ArrayRef',
    is      => 'ro',
    default => sub { [] },
    traits  => ['Array'],
    handles => { has_tags => 'count', },
);

sub _filepath {
    my $self  = shift;
    my $start = $self->start;
    my $name  = $self->project;
    $name =~ s/\W/_/g;
    $name =~ s/_+/_/g;
    $name =~ s/^_//;
    $name =~ s/_$//;
    return (
        $start->year,
        sprintf( '%02d', $start->month ),
        $start->strftime("%Y%m%d-%H%M%S") . '_' . $name . '.trc'
    );
}

sub _calc_duration {
    my ( $self, $stop ) = @_;
    $stop ||= $self->stop;
    my $delta = $stop->subtract_datetime( $self->start );
    $self->seconds( $dtf_sec->format_duration($delta) );
    $self->duration( $dtf_dur->format_duration($delta) );
}

sub storage_location {
    my ( $self, $home ) = @_;
    my $file = $home->file( $self->_filepath );
    $file->parent->mkpath;
    return $file;
}

sub description_short {
    my ($self) = @_;
    my $description = $self->description;
    return unless $description;

    $description =~ s/(.{40}[[:alnum:]]*).+$/$1.../;
    $description =~ s/^(.{50}).+$/$1.../;
    $description =~ s/\.{3,}$/.../;
    return $description;
}

sub save {
    my ( $self, $home ) = @_;

    my $file = $self->storage_location($home)->stringify;
    $self->store($file);
    return $file;
}

sub current {
    my ( $class, $home ) = @_;
    $class->_load_from_link( $home, 'current' );
}

sub previous {
    my ( $class, $home ) = @_;
    $class->_load_from_link( $home, 'previous' );
}

sub _load_from_link {
    my ( $class, $home, $link ) = @_;
    my $file = $home->file($link);
    return unless -e $file;
    my $linked_file = $file->slurp( chomp => 1 );
    return unless -e $linked_file;
    return $class->load($linked_file);
}

sub say_project_tags {
    my $self = shift;

    my $tags = $self->tags;
    my $rv   = $self->project;
    $rv .= ' (' . join( ', ', @$tags ) . ')' if @$tags;
    $rv .= ' ' . $self->description if $self->description;
    return $rv;
}

sub do_start {
    my ( $self, $home ) = @_;

    my $saved_to = $self->save($home);

    my $fh = $home->file('current')->openw;
    say $fh $saved_to;
    close $fh;

    say "Started working on "
        . $self->say_project_tags . " at "
        . $self->start->hms;
}

sub rounded_minutes {
    my $self = shift;
    my $sec  = $self->seconds;
    my $rest = 60 - $sec % 60;
    my $min  = ( $sec + $rest ) / 60;
    return $min;
}

sub get_detail {
    my ( $self, $level ) = @_;

    my $detail = {};
    if ( $level eq 'tag' || $level eq 'description' ) {
        $detail->{tags} = $self->tags;
    }
    if ( $level eq 'description' ) {
        $detail->{desc} = $self->description;
    }
    return $detail;
}

__PACKAGE__->meta->make_immutable;
1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::TimeTracker::Data::Task - App::TimeTracker Task storage

=head1 VERSION

version 2.025

=head1 DESCRIPTION

Rather boring class implementing a Task object. Mainly used for storage etc.

=head1 AUTHOR

Thomas Klausner <domm@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 - 2018 by Thomas Klausner.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut