package API::Command::GetDownloadStatement;
use strict;
use warnings;

use Apache2::Const qw(OK FORBIDDEN HTTP_OK HTTP_UNAUTHORIZED HTTP_NOT_FOUND HTTP_NOT_IMPLEMENTED);

use File::Temp;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::DB::Item::PayeeType;
use RSApache::Response::DownloadZipDirectory;
use RSApache::Response::Download;

use lib '/app/tools/api/lib';
use API::Response;
use API::DB::Item::PortalPayee;
use API::Util;

use lib '/app/tools/rps/lib';
use RPS::RoyaltyRun::Status;
use RPS::Statement::Artist::StatementFull;
use RPS::Statement::Mechanical::US::PDF;
use RPS::Statement::Mechanical::US::Text;
use RPS::Statement::Mechanical::US::StatementFull;
use RPS::ArtistRoyalty::ArtistRoyaltyRun;
use RPS::Mechanical::US::MechanicalRun;

use RPS::DB::Item::ArtistRoyaltyStatement;
use RPS::DB::Item::MechanicalStatement;

use base 'API::Command';

sub area { return "client"; }
sub cmd  { return "statement"; }

# This command is the endpoint for downloading a single statement, and is visible
# to the user as a download link on the UI statements page.  It requires a valid
# session.  The user must have appropriate permissions to download the statement.
#
#   GET /client/:clientId/runType/:runTypeId/statement/:statementId?format=fmt
#
# In order to download the specified statement, the following criteria must be met:
#   * A valid session cookie must be present;
#   * The portal user must exist for the user/client combination;
#   * The requested statement must be for the client payee associated with the portal user.
#

sub execute {
    my ($self) = @_;

    # Make sure we have a valid session
    my $response = $self->SUPER::execute();
    return $response if ($response);

    my $userID   = $self->{userID};    # derived from session (see SUPER::execute)

    my $clientID = $self->getRouteParam(":clientId")    || 0;
    my $runType  = $self->getRouteParam(":runTypeId")   || '';         # artist | publisher | ca_publisher
    my $stmtID   = $self->getRouteParam(":statementId") || 0;
    my $format   = lc( $self->getParam("format")        || "pdf" );    # pdf | xls | txt

    my $errorResponse = $self->_validate($userID, $clientID, $runType);
    return $errorResponse if $errorResponse;


    my $oClient = Common::DB::Item::Client->Lookup( client_id => $clientID, );
    my $clientName = API::Util->cleanName( $oClient->display_name );

    my $appSingleton = Common::RSApp->new_temporary( clientID => $clientID );
    my $dbo = Common::RSApp::GetClientDB();


    my ($oStatement, $error) = $self->_getStatement($runType, $stmtID);
    return $error if $error;

    $error = $self->_checkStatementRunStatus($runType, $oStatement);
    return $error if $error;

    $error = $self->_checkPayeeUserPermissions($userID, $clientID, $runType, $oStatement);
    return $error if $error;

    my $response = $self->_downloadStatementFile($clientName, $clientID, $format, $runType, $oStatement);
    return $response;
}

sub _validate {
    my ($self, $userID, $clientID, $runType) = @_;

    # Make sure user can access client (e.g., does a valid portal user exist?)
    if( !$self->canAccess($userID, $clientID) ) {
        return $self->createResponse(
            status => HTTP_UNAUTHORIZED,
            data   => { msg => "user not authorized for client $clientID" }
        );
    }

    my $allowedRunTypes = join '|', $self->getAllowedRunTypes();
    unless ( $runType =~ /^(?:$allowedRunTypes)$/ ) {
        return $self->createResponse(
            status => HTTP_NOT_IMPLEMENTED,
            data   => { msg => "Invalid runtype" }
        );
    }

    return;
}

sub _getStatement {
    my ($self, $runType, $stmtID) = @_;

    my $oStatement;
    if ( $runType =~ /^artist$/i ) {
        $oStatement = RPS::DB::Item::ArtistRoyaltyStatement->Lookup( artist_royalty_statement_id => $stmtID );
    } elsif ( $runType =~ /^publisher$/i ) {
        $oStatement = RPS::DB::Item::MechanicalStatement->Lookup( mechanical_statement_id => $stmtID );
    }

    if ( !$oStatement ) {
        return undef, $self->createResponse(
            status => HTTP_NOT_IMPLEMENTED,
            data   => { msg => "Non existent statement" }
        );
    }

    return $oStatement;
}

sub _checkStatementRunStatus{
    my ($self, $runType, $oStatement) = @_;

    my $oRun;
    if ( $runType =~ /^artist$/i ) {
        $oRun = RPS::ArtistRoyalty::ArtistRoyaltyRun->new(
            artistRoyaltyRunID => $oStatement->artist_royalty_run_id
        );
    } elsif ( $runType =~ /^publisher$/i ) {
        $oRun = RPS::Mechanical::US::MechanicalRun->new(
            mechanicalRunID => $oStatement->mechanical_run_id,
            payorID => $oStatement->payor_id
        );
    }

    if ( !$oRun ) {
        return $self->createResponse(
            status => HTTP_NOT_FOUND,
            data   => { msg => "Non existent run" }
        );
    }

    if (   RPS::RoyaltyRun::Status::kCommitted != $oRun->Status()
        && RPS::RoyaltyRun::Status::kClosed != $oRun->Status() ) {

        return $self->createResponse(
            status => HTTP_NOT_IMPLEMENTED,
            data   => "Statement not on a closed or committed run",
        );
    }

    return;
}

sub _checkPayeeUserPermissions {
    my ($self, $userID, $clientID, $runType, $oStatement) = @_;

    my %payeeIDFieldMap = (
        artist       => 'payee_id',
        publisher    => 'publisher_id',
        ca_publisher => 'ca_publisher_id',
    );

    my $payeeTypeID = $self->getPayeeTypeByRunType($runType);
    my $payeeIDField = $payeeIDFieldMap{$runType};
    my $payeeID = $oStatement->$payeeIDField();

    my $oPortalPayee = API::DB::Item::PortalPayee->Lookup(
        user_id    => $userID,
        client_id  => $clientID,
        payee_id   => $payeeID,
        payee_type => $payeeTypeID,
    );

    if ( !$oPortalPayee ) {
        return $self->createResponse(
            status => HTTP_NOT_FOUND,
            data   => "No portal payee user found for client_id $clientID, payee_id $payeeID, payee_type $payeeTypeID",
        );
    }

    if ( $oPortalPayee->date_confirmed eq '0000-00-00 00:00:00' ) {
        return $self->createResponse(
            status => HTTP_UNAUTHORIZED,
            data   => "Portal user $userID has not been confirmed for payee_id $payeeID, payee_type $payeeTypeID",
        );
    }

    # this block for additional recipients only
    my $recipientID = $oPortalPayee->statement_recipient_id;
    return unless $recipientID;

    if ( $oStatement->date_sent lt $oPortalPayee->date_invited ) {
        return $self->createResponse(
            status => HTTP_UNAUTHORIZED,
            data   => "Portal user $userID is not allowed to see the statement, payee_id $payeeID, payee_type $payeeTypeID, recipient_id $recipientID",
        );
    }

    return;
}

sub _downloadStatementFile {
    my ($self, $clientName, $clientID, $format, $runType, $oStatement) = @_;

    $ENV{HOME} = '/';

    my ($path, $fileName);
    if ( $runType =~ /^artist$/i ) {
        ($path, $fileName) = $self->_getArtistStatementFile($clientName, $clientID, $format, $runType, $oStatement);
    } elsif ( $runType =~ /^publisher$/i ) {
        ($path, $fileName) = $self->_getPublisherStatementFile($clientName, $clientID, $format, $runType, $oStatement);
    } else {
        return $self->createResponse(
            status => HTTP_NOT_IMPLEMENTED,
            data   => { msg => "Unable to download file" }
        );
    }

    my $fh = new IO::File;
    open( $fh, $path ) or die "ERROR: Unable to open $path for reading: $!";
    my @stat        = $fh->stat;
    my $fileSize    = $stat[7];
    my $contentType = 'application/octet-stream';
    my $response    = RSApache::Response::Download->new( $fh, $contentType, $fileName, $fileSize );

    return $response;
}

sub _getArtistStatementFile {
    my ($self, $clientName, $clientID, $format, $runType, $oStatement) = @_;

    my $runID = $oStatement->artist_royalty_run_id;

    my $tmpDirPath   = "/tmp/$runType" . "_" . $clientID . "_" . $runID . "XXXXXXXX";
    my $tempdir      = File::Temp::tempdir( $tmpDirPath, CLEANUP => 1 );
    my $baseFilePath = RPS::Statement::Artist::PDF::StatementPathFromRunID($runID);
    my $clientPrefix = $clientName . '__run_' . $runID;

    my $fullPath;
    my $prettyFileName;
    if ( $format eq 'pdf' ) {
        $prettyFileName = RPS::Statement::Artist::PDF::PrettyStatementFileNameFromDBItem($oStatement);
        $prettyFileName = $clientPrefix . '__' . $prettyFileName;

        my $uglyFileName = RPS::Statement::Artist::PDF::StatementFileNameFromDBItem($oStatement);

        system("ln -s $baseFilePath/$uglyFileName $tempdir/$prettyFileName");
        $fullPath = "$baseFilePath/$uglyFileName";
    }

    if ( $format eq 'xls' ) {
        $prettyFileName = RPS::Statement::Artist::Excel::PrettyStatementFileNameFromDBItem($oStatement);
        $prettyFileName = $clientPrefix . '__' . $prettyFileName;

        my $uglyFileName = RPS::Statement::Artist::Excel::StatementFileNameFromDBItem($oStatement);

        system("ln -s $baseFilePath/$uglyFileName $tempdir/$prettyFileName");
        $fullPath = "$baseFilePath/$uglyFileName";
    }

    return $fullPath, $prettyFileName;
}

sub _getPublisherStatementFile {
    my ($self, $clientName, $clientID, $format, $runType, $oStatement) = @_;

    my $runID = $oStatement->mechanical_run_id;

    my $tmpDirPath   = "/tmp/$runType" . "_" . $clientID . "_" . $runID . "XXXXXXXX";
    my $tempdir      = File::Temp::tempdir( $tmpDirPath, CLEANUP => 1 );
    my $baseFilePath = RPS::Statement::Mechanical::US::PDF->StatementPathFromRunID($runID);
    my $clientPrefix = $clientName . '__run_' . $runID;

    my $fullPath;
    my $prettyFileName;
    if ( $format eq 'pdf' ) {
        $prettyFileName = RPS::Statement::Mechanical::US::PDF->PrettyStatementFileNameFromDBItem($oStatement);
        $prettyFileName = $clientPrefix . '__' . $prettyFileName;

        my $uglyFileName = RPS::Statement::Mechanical::US::PDF->StatementFileNameFromDBItem($oStatement);

        system("ln -s $baseFilePath/$uglyFileName $tempdir/$prettyFileName");
        $fullPath = "$baseFilePath/$uglyFileName";
    }

    if ( $format eq 'txt' ) {
        $prettyFileName = RPS::Statement::Mechanical::US::Text->PrettyStatementFileNameFromDBItem($oStatement);
        $prettyFileName = $clientPrefix . '__' . $prettyFileName;

        my $uglyFileName = RPS::Statement::Mechanical::US::Text->StatementFileNameFromDBItem($oStatement);

        system("ln -s $baseFilePath/$uglyFileName $tempdir/$prettyFileName");
        $fullPath = "$baseFilePath/$uglyFileName";
    }

    return $fullPath, $prettyFileName;
}


1;
