#!/usr/bin/perl
# fetch_abacus_report.pl -- script to download an Orchard sales report
# from an S3 location containing sales files generated by querying
# Snowflake.
#
# Arguments:
#   -c clientID
#
#   -a Orchard AccountID
#   -p Orchard PeriodID
#   --contract ContractID

#   -s s3_key
#      Name of source S3 path to the Abacus sales file.
#      Example:
#        -s export/318/32952/339780/data_01bde2a6-0810-ee21-0001-4d0360e46f73_683_7_0.csv.gz
#   -f name
#      Specifies the RPS name to use when copying the file specified via '-s'
#      Example:
#        -f 20250723_Jun25_fullreport_house_arrest_mxmtoon_32952_339780.tsv.gz
#   
#   -u RPS user_id to receive Orchard notifications.  We don't send
#      notifications from this script, but rather set it in the RPS
#      file entry so that the file import (done elsewhere) will know
#      who to send notifications to.
#
#   -t truncate downloaded reports.  The reports can be over a million
#      lines long, and can take significant time to process.  When the
#      downloaded archive file is unpacked, we can optionally truncate
#      the sales report to 100 lines prior to uploading it into RPS.
#      This makes it much easier to import and troubleshoot file
#      ingestion issues.  THIS OPTION IS FOR DEVELOPMENT TESTING ONLY.
#
# This script will check if an Abacus sales report exists for the specified period/vendor/contract.
# This check is done against the RSCOMMON.orchard_report table.  If we don't have an client entry
# for the specified period/vendor/contract, or if we do have a client entry but no file_id, then
# we'll download the file from S3 and upload it into the client's site.  The orchard_report entry
# will be updated for the uploaded file.  It is the caller's responsibility to create an import job
# for the file.
#
use strict;
use warnings;

use Data::Dumper;
use Getopt::Long;
use File::Basename;
use File::Path qw( make_path rmtree);

use Net::Amazon::S3;
use Net::Amazon::S3::Client;
use Net::Amazon::S3::Bucket;


use lib '/app/tools/common/lib';
use Common::Assert;
use Common::RSDB;
use Common::RSApp;
use Common::Util;
use Common::Log;
use Common::DB::Item::OrchardReport;
use Common::Amazon::Config;

use lib '/app/tools/raptor/lib';
use Raptor::Config;

use lib '/app/tools/data_classes/lib';
use File::File;

use lib '/app/tools/rps/lib';
use RPS::File::File;
use RPS::Sale::File;

# Downloaded reports will be stored and unpacked in the following directory
use constant kDownloadDir => '/app/data/orchard_fetch';

my $prog = basename($0);

my %args = ();
parseCommandLine( \%args );
my $clientID  = $args{clientID};
my $accountID = $args{accountID};
my $periodID  = $args{periodID};
my $userID    = $args{userID};
my $truncate  = $args{truncate};
my $logLevel  = $args{logLevel};
my $contractID = $args{contractID};
my $forceDownload = $args{force};

my $source   = $args{source};
my $filename = $args{filename};

assert($clientID);
assert($source);
assert($filename);
assert($accountID);
assert($periodID);
#assert($contractID);
assert($userID);

my $appSingleton = Common::RSApp->new( clientID => $clientID );

Log->init( level => 'error' );
Log->setLogLevel($logLevel) if ($logLevel);


# Create the local directory to contain downloaded report(s)
# A vendor may have contract-specific reports for a given period. we'll store
# them all in the same period directory for the vendor.
#
my $downloadDir = kDownloadDir . '/' . $clientID . '/' . $accountID . '/' . $periodID . '/';    #   .../<clientID>/<accountID>/<periodID>

if ( !-d $downloadDir ) {
    make_path $downloadDir or die "Failed to create output directory: $downloadDir";
}


Log->debug( "###### Setting up S3 connection ..." );

# Setup S3 connection
#
my ( $access, $secret, $region ) = get_aws_creds('orch-transfer');    # See Common/Amazon/Config.yml

my $s3 = Net::Amazon::S3->new(
    aws_access_key_id     => $access,
    aws_secret_access_key => $secret,
    retry                 => 1,
);

my $s3bucket = 'snowflake-sme-abacus-export';
my $bucket = $s3->bucket($s3bucket);


print "#### Checking source $source ...\n";

# Make sure we haven't already seen this Abacus report.  If we haven't seen it, or
# we have seen it but don't yet have an RPS file_id for it then we'll process the
# download the file from S3 and create an RPS file object for it.  Otherwise exit
# the script.

my $report = Common::DB::Item::OrchardReport->Lookup(
    client_id => $clientID,
    account_id => $accountID,
    period => $periodID,
    contract_id => $contractID
);

if ( $report ) {
    Log->info( "$0: Report found for c($clientID) a($accountID) p($periodID) c($contractID)");
    if ( $report->file_id != 0 ) {
        Log->info( "   reportID " . $report->report_id . " has fileID " . $report->file_id . ", skipping download ...");
        exit;
    } else {
        Log->info( "   reportID " . $report->report_id . " has fileID " . $report->file_id . ", downloading file ...");
    }
}

if ( !$report ) {
    Log->info( "$0: No report found for c($clientID) a($accountID) p($periodID) c($contractID)");
    $report = Common::DB::Item::OrchardReport->Create(
        client_id => $clientID,
        account_id => $accountID,
        period => $periodID,
        contract_id => $contractID
    );

    $report->save();
    Log->info( "$0: Created reportID " . $report->report_id  );
}


# At this point we just created an OrchardReport for the client/account/period/contract.
# Let's copy the file from S3 to a local directory.

my $localFilepath = $downloadDir . $filename;
Log->info("copying $source to $localFilepath ...");

if ( ! -e $localFilepath || (-e $localFilepath && $forceDownload) ) {
    my $response = $bucket->get_key_filename( $source, 'GET', $localFilepath );
    if ( ! $response ) {
        Log->error("$0: S3 File not found: $source");
        exit;
    }
}

my $textFilepath = $localFilepath;    # full path to the ZIP file containing report
($textFilepath) = $1 if ( $localFilepath =~ /(.+)\.gz$/i );

my $textFilename = basename($textFilepath);    # name of the unzipped text report
Log->info("    \$textFilepath = $textFilepath");

if ( !-e $textFilepath ) {
    Log->debug("$prog: uncompressing '$localFilepath'");
    chdir($downloadDir);
    `gzip -d $localFilepath`;
    Log->debug("   done uncompressing '$localFilepath'");
    $report->status(Common::DB::Item::OrchardReport::kFileUnpacked);
    $report->save;
}

if ($truncate) {
    my $tmpFile = 'tmp_' . $$;
    Log->debug("$prog: truncating file to 100 lines");
    chdir($downloadDir);
    `cp $textFilename $tmpFile`;
    `head -100 $tmpFile > $textFilename`;    # limit the report to 100 lines for testing
    `rm $tmpFile`;
}


my $file;
my $fileID;
my $fileStatus;

chdir($downloadDir);

# Upload the file from the local FS into RPS if we don't
# already have it in our system.
#
my $files  = RPS::File::Files->new();
my $md5sum = Common::Util::md5sum($textFilepath);

if ( $files->GetByOrigName( file_name => $textFilename ) || $files->GetByMD5( md5_sum => $md5sum ) ) {
    $file       = $files->GetNext();
    $fileID     = $file->FileID();
    $fileStatus = $file->FileStatus();
    Log->error("DUPLICATE_FILE: client $clientID fileID $fileID (status $fileStatus) exists for '$textFilename'");

    # If the file is in RPS and it looks like it's being (or has been)
    # processed, then we'll just flag the report and update the notes
    # field with the RPS file status.
    #
    if ( $fileStatus != File::File::STATUS_NEW ) {
        $report->status(Common::DB::Item::OrchardReport::kFileError);
        $report->notes( "File $fileID already exists with status " . $fileStatus );
        $report->file_id($fileID);
        $report->save;
        exit(0);
    }
} else {
    Log->info("Uploading '$textFilename' into RPS ...");
    $fileID     = upload($textFilename);
    $file       = RPS::File::File->new( file_id => $fileID, client_id => $clientID );
    $fileStatus = $file->FileStatus();

    unlink $textFilepath  or die "Unable to unlink $textFilepath: $!";     # text file
    rmtree($downloadDir)  or die "Unable to delete $downloadDir: $!";      # download directory

    # The sales file has been added to the clients 'file' table and a physical
    # copy is now in /app/shared/sale_import.

}

# Set the UserID to the id of client's notification email address (see FB18135).
#
$file->UserID($userID);
$file->Save();

# Update the orchard report info
#
$report->status(Common::DB::Item::OrchardReport::kFileImportReady);
$report->file_id($fileID);
$report->save();

if ( !$fileID ) {
    Log->error("$prog: #### No fileID found for '$textFilename' !!!");
} else {
    Log->info("$prog: #### File uploaded, fileID $fileID (status $fileStatus)");
}

sub upload {
    my $file = shift;
    my $fh;
    open( $fh, $file ) or die "can't open file: $!\n";
    my $md5sum = Common::Util::md5sum($fh);
    close($fh);

    return RPS::Sale::File::SimpleUploadFromShell(
        client_id => $clientID,
        filepath  => $file,
        md5_sum   => $md5sum,
    );
}

sub parseCommandLine {
    my ($a) = @_;
    my $clientID;
    my $accountID;
    my $periodID;
    my $userID;

    my $contractID; # Workstation contractID from Abacus; assume integer
    my $source;     # Where to get the file from (should be S3)
    my $filename;   # The name to use in RPS
    my $forceDownload;

    my $truncate;
    my $logLevel;

    GetOptions(
        'c|client_id=i'  => \$clientID,
        'a|account_id=i' => \$accountID,
        'p|period_id=s'  => \$periodID,    # can be DDD or DDD,DDD,DDD
        'u|user_id=i'    => \$userID,

        's|source=s'     => \$source,
        'f|file=s'       => \$filename,

        't|truncate'     => \$truncate,
        'l|loglevel=s'   => \$logLevel,
        'contract=s'     => \$contractID,
        'force'          => \$forceDownload,
    );

    my $msg;
    unless ( $clientID && $clientID =~ /^\d+$/ && $clientID > 0 ) {
        my $e = "You must enter a valid clientID";
        $msg = ($msg) ? "$msg; $e" : $e;
    }
    unless ( $userID && $userID =~ /^\d+$/ && $userID > 0 ) {
        my $e = "You must enter a valid userID";
        $msg = ($msg) ? "$msg; $e" : $e;
    }
    unless ( $accountID && $accountID =~ /^\d+$/ && $accountID > 0 ) {
        my $e = "You must enter a valid accountID";
        $msg = ($msg) ? "$msg; $e" : $e;
    }
    unless (
        $periodID
        && (   $periodID =~ /^\d+$/
            || $periodID =~ /^\d+,\d+,\d+$/ )
      ) {
        my $e = "You must enter a valid periodID";
        $msg = ($msg) ? "$msg; $e" : $e;
    }

    die( usage($msg) ) if ($msg);

    $a->{clientID}  = $clientID;

    # The accountID and periodID are used to determine where to copy the 'filename' to

    $a->{accountID} = $accountID;
    $a->{periodID}  = $periodID;
    $a->{contractID} = $contractID;

    $a->{userID}    = $userID;

    $a->{source}    = $source;
    $a->{filename}  = $filename;
    $a->{force}     = $forceDownload;

    $a->{truncate}  = $truncate;
    $a->{logLevel}  = $logLevel;
}

sub usage {
    my $errstr = shift;
    my $text = ($errstr) ? "ERROR: $errstr\n" : '';
    $text .= "Usage $0 -c <clientID> -a <accountID> -p <periodID> --contract contractID -u <userID> -o s3path -f rpsFileName [-l debuglevel] [-t]";
}

###
1;#
###
