#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long;
use File::Copy 'mv';
use File::Basename;
use File::Path 'make_path';
use File::Spec;
use Date::Find 'find_ymd', 'guess_ymd', '%date_type', '%longname';
use Pod::Usage;

our $VERSION = '0.03';

=head1 NAME

move-year - Move files to directories according to a date in their filename

=cut

my $date_type;
GetOptions(
    'dry-run|n'            => \my $dry_run,
    'verbose'              => \my $verbose,
    'help'                 => \my $help,

    'date-regex|d=s'       => \my $date_regex,
    'date-regex-order|o=s' => \my $date_regex_order,
    'date-type|t=s'        => \$date_type,
    'ymd'                  => sub { $date_type = 'ymd' },
    'dmy'                  => sub { $date_type = 'dmy' },
    'ym'                   => sub { $date_type = 'ym' },
    'my'                   => sub { $date_type = 'my' },
    'y'                    => sub { $date_type = 'y' },
    'force|f'              => \my $force,
    'create'               => \my $create_directories,
    'directory-style|s=s'  => \my $directory_style,
    'part-separator'       => \my $part_separator,

    'i'                    => \my $dont_overwrite,

    'strict'               => \my $strict,
) or pod2usage(2);
pod2usage(1) if $help;
pod2usage("$0: No files given.")  if (@ARGV == 0);

=head1 SYNOPSIS

  move-year --create foo-2025.pdf /home/user/documents/
  # creates /home/user/documents/2025
  # Moves foo-2025.pdf to /home/user/documents/2025/foo-2025.pdf

  move-year --create --directory-style=sprintf foo-2025.pdf "/home/user/documents/documents %y/"
  # creates /home/user/documents/documents 2025/
  # Moves foo-2025.pdf to /home/user/documents/documents 2025/foo-2025.pdf

=head1 OPTIONS

=over 4

=item B<--dry-run>, B<-n>

Only output actions, do not perform them. Implies B<--verbose>.

=item B<--verbose>

Output the actions.

=item B<--force>, B<-f>

Overwrite files if they already exist.

=item B<--i>, B<-i>

Don't overwrite files if they already exist.

=item B<--strict>

Be strict in the detection. When a file fails to detect properly, stop
the program.

=item B<--create>

Create directories if they don't exist.

=item B<--directory-style>

Elements of the intermediate directory to be created. Defaults to B<y>.

Use B<--directory-style=sprintf> to specify your own template. B<%y>, B<%m> and B<%d>
will be replaced by their respective values in the target directory name.

=item B<--part-separator>

Separator for the elements of the intermediate directory. Defaults to B<->.

=item B<--date-type>, B<t>

Type of the date to find. May be a combination of B<y>, B<m>, B<x>, B<d>.
Shorthands exist as the options C<--ymd>, C<--dmy>. B<x> stands for the month
name.

    ymd
    dmy
    ym
    my
    y

=item B<--date-regex>, B<-d>

Regular expression to extract a date. This should be a capturing regular
expression that captures into named groups C<year>, C<month>, C<monthname>
or C<day>.

Most likely you want to use a premade one instead.

=item B<--date-regex-order>, B<-o>

Order of year, month and day in your filename strings if your custom
regular expression does not use named captures.

Default is C<ymd>.

=back

=cut

$verbose //= $dry_run;

$part_separator //= '-';
$directory_style //= 'y';

$date_type //= 'ymd';

$create_directories //= $ENV{'MOVE_YEAR_CREATE'};

my $target = ( -f $ARGV[-1] and exists $ENV{'MOVE_YEAR_TARGET'})
  ? $ENV{'MOVE_YEAR_TARGET'}
  : pop(@ARGV);

if( ! -d $target and $directory_style ne 'sprintf') {
    die "'$target' is not a directory";
}

sub move_file {
    my( $source,$target,%ymd ) = @_;
    my $target_dir;

    if( $directory_style eq 'sprintf' ) {
        $target_dir = $target =~ s/%([ymd])/$ymd{ $longname{ $1 }}/reg;

    } else {

        $target_dir = File::Spec->catdir( $target,
                        map { $ymd{ $_ } }
                        map { $longname{ $_ } }
                        split //, $directory_style
        );
    }
    if( ! -d $target_dir ) {
        if( $create_directories ) {
            if( $verbose ) {
                print "Creating '$target_dir'\n";
            }
            if( ! $dry_run ) {
                make_path $target_dir;
            };
        } else {
            die "'$target_dir' does not exist for $source (forgot --create ?)\n";
        }
    }
    my $name = basename($source);
    my $target_file = "$target_dir/$name";
    if(($dry_run || -d $target_dir)) {
        if( not( -e $target_file) || $force) {
            if( $verbose ) {
                print "Moving '$source' to '$target_file'\n";
            }
            if( ! $dry_run ) {
                mv $source => $target_file or warn "$source: $!";
            };
        } else {
            if( $verbose ) {
                print "'$target_file' already exists, skipped\n";
            }
        }
    }
}

for my $file (@ARGV) {
    my $ymd;
    if( $date_regex ) {
        $ymd = find_ymd( $date_regex, $file, $date_regex_order );

    } else {
        $ymd = guess_ymd( $file, components => $date_type, mode => 'strict' );
    }
    if( keys %$ymd ) {
        move_file( $file, $target, %$ymd )
    } elsif( $strict ) {
        die "Could not find a date in '$file'\n";
    } else {
        warn "Skipping '$file' (no date found)\n";
    }
}

=head1 ENVIRONMENT VARIABLES

=over 4

=item B<MOVE_YEAR_CREATE>

Set the C<<--create>> option from the environment

=item B<MOVE_YEAR_TARGET>

Set the target directory

=back

=cut
