#!/usr/bin/env perl

use Carp;
use warnings;
use strict;

use vars qw( $VERSION );
$VERSION = '0.01';

use Date::Parse;

use IPC::Cmd 0.36 qw( can_run run );
use PS::IPP::Metadata::Config;
use PS::IPP::Config 1.01 qw( :standard );

my $ipprc = PS::IPP::Config->new(); # IPP configuration

use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use Pod::Usage qw( pod2usage );

# Look for programs we need
my $missing_tools;
my $dsproductls = can_run('dsproductls') or (warn "Can't find dsproductls" and $missing_tools = 1);
my $dsreg   = can_run('dsreg') or (warn "Can't find dsreg.pl" and $missing_tools = 1);
if ($missing_tools) {
    warn("Can't find required tools.");
    exit($PS_EXIT_CONFIG_ERROR);
}

my ($product, $datastore, $last_fileset, $keep_files, $verbose);

# If supplied, retention_days is the number of days that filesets in a data store will be preserved.
# The use_configured_publish_retention_days option tells us to get retention_days from the site config.
# The later is only used if retention-days is not supplied in the options.
# NOTE: if last_fileset is also given, clean up will stop after that fileset irrespective of whether
# subsequent filesets are older than the retention time.

my ($retention_days, $use_publish_retention_days);

# Parse the command-line arguments
GetOptions(
           'product=s'      => \$product,      # product name
           'datastore=s'    => \$datastore,    # datastore uri
           'last_fileset=s' => \$last_fileset, # last fileset to delete
           'last-fileset=s' => \$last_fileset, # more modern spelling for last option
           'retention-days=s'   => \$retention_days, # see above
           'use-configured-publish-retention' => \$use_publish_retention_days, #see above
           'no_rm'          => \$keep_files,   # remove fileset but leave files in place
           'no-rm'          => \$keep_files,   # more modern spelling for last option
           'verbose'        => \$verbose,      # Print stuff?
           ) or pod2usage( 2 );

pod2usage( -msg => "Unknown option: @ARGV\n", -exitval => 2 ) if @ARGV;
pod2usage( -msg => "Required options: --product\n",
           -exitval => 3) unless
    defined $product;

if (!$datastore) {
    $datastore = metadataLookupStr($ipprc->{_siteConfig}, "DATA_STORE_ROOT_URL");
    die ("Unable to find DATA_STORE_ROOT_URL in siteConfig\n") unless $datastore;
}

my $uri = "$datastore/$product/index.txt";

# XXX: I'm not sure why this bracket and indentation is here
{
    my $command = "$dsproductls --uri $uri --extra --timeout 150";

    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
        run(command => $command, verbose => $verbose);
    unless ($success) {
        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
        die("Unable to perform $command: $error_code");
    }

    my @lines = split "\n", join("", @$stdout_buf);

    if ((scalar @lines) eq 1) {
        print "no filesets found in $uri\n";
        exit 0;
    }

    # if retention_days is specified, use that. Otherwise if requested use the publish config value from the siteConfig
    if (!$retention_days and $use_publish_retention_days) {
        $retention_days = metadataLookupS32($ipprc->{_siteConfig}, "DATA_STORE_PUBLISH_RETENTION_DAYS");
        die ("required value DATA_STORE_PUBLISH_RETENTION_DAYS not found in siteConfig\n") unless defined $retention_days;
    }

    # if $retention_days is set we are to clean only filesets registered before
    # that date/time. Convert the limit to seconds.
    my $clean_before;
    if ($retention_days) {
        $clean_before = time() - $retention_days * 86400;
    }

    foreach my $line (@lines) {
        chomp $line;
        next if $line =~ /#/;

        my ($fsuri, $fs_name, $registered, $fs_type, $target_id, $stage, $stage_id) = split " ", $line;

        if ($clean_before) {
            # registered is in the format YYYY-MM-DDTHH:MM:SSZ. Date::Parse::str2time handles this.
            my $ticks = str2time($registered);
            if ($ticks > $clean_before) {
                # This fileset and therefore subsequent ones in the list was registered after our end
                # date. We're done.
                last;
            }
        }

        $command = "$dsreg --del $fs_name --product $product";
        
        $command .= " --rm" unless $keep_files;

        print "$command\n";

        my $rc = system $command;
        if ($rc) {
            my $error_code = $rc >> 8;
            die "$command failed with $error_code $rc\n";
        }
        last if $last_fileset and ($fs_name eq $last_fileset);
    }
}

__END__

=pod
=head1 NAME

cleandsproduct.pl - cleans up a data store product

=head1 SYNOPSIS

cleandsproduct.pl --product <product name> [--last-fileset <filesetid>] [--no-rm] [--retention-days <days>] [--use-configured-publish-retention]

=head1 DESCRIPTION

Uses L<dsproductls> to get a listing of all of the filesets in a data store product, then the program L<dsreg> is used to delete
the filesets from the data store directory and their representation in the database.

By default, all filesets are deleted. Certain options may be used to stop the cleanup after a given fileset 
or once all remaining filesets are newer than some time.

=head1 OPTIONS

=over 4

=item --product <product name>

The name of the product to be cleaned up.

Required.

=item --retention-days <days>

The minimum number of days old for filesets to be cleaned.
Optional.

=item --use-configured-publish-retention

Get retention-days value from the SiteConfig
Optional.

=item --last_fileset <filesetid>

The FileSet ID of the last FileSet to be cleaned. Note: if the specified fileset is newer than the retention time
cleanup stops after cleaning that fileset.
Optional.

=item --no-rm

Update the database but do not remove the files on disk. The files will thus still exist, but they will be invisible
through the data store interface.

=head1 Configuration Values

The following configuration values are used to set certain default values.

=item DATA_STORE_ROOT_URL 

The http address of the data store. Required.

=item DATA_STORE_PUBLISH_RETENTION_DAYS

The number of days that filesets in the publishing data stores should be preserved. Only used and required if
the option --use-configured-publish-retention is supplied.

=back

=head1 CREDITS

Bill Sweeney IfA

=head1 SUPPORT

Please contact the author directly via e-mail.

=head1 AUTHOR

Bill Sweeney bills@ifa.hawaii.edu

=head1 COPYRIGHT

Copyright (C) 2011 - 2016  University of Hawaii IfA.


=head1 SEE ALSO

L<dsreg> L<dsproductls>

=cut
