#------------------------------------------------------------
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package BookPub::POS::Tracker::File;

use strict;
use warnings;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Client;
use Common::Util;
use RSApache::Message;
use base 'Common::FormObject';

use lib '/app/tools/bookpub/lib';
use BookPub::DB::Item::POSFile;
use BookPub::DB::Item::POSSale;
use BookPub::DB::Item::InputConversionRate;
use BookPub::Tracker::Service::ServiceWithConfig;

sub _init {
    my ( $self, %args ) = @_;

    $self->SUPER::_init(%args);

    my %properties;
    if ( $args{dbItem} ) {
        $self->_propertiesFromDBItem( \%properties, $args{dbItem} );
    } elsif ( defined $args{posFileID} ) {
        $self->_propertiesFromDB( \%properties, $args{posFileID} );
    }

    my $rval = $self->_initProperties( \%properties );

    return $rval;
}

sub _propertiesFromDB {
    my ( $self, $hrProperties, $fileID ) = @_;
    assert($fileID);

    my $dbItem = BookPub::DB::Item::POSFile->Lookup( pos_file_id => $fileID );

    return unless ($dbItem);

    $self->_propertiesFromDBItem( $hrProperties, $dbItem );
}

sub _propertiesFromDBItem {
    my ( $self, $hrProperties, $dbItem ) = @_;
    assert($dbItem);

    # Save a reference to the dbItem we used, so we can tell later if this object
    # exists in the database already...
    #
    $self->{_dbItem} = $dbItem;

    $hrProperties->{pos_file_id}    = $dbItem->pos_file_id;
    $hrProperties->{service_id}     = $dbItem->service_id;
    $hrProperties->{type_id}        = $dbItem->type_id;
    $hrProperties->{version_num}    = $dbItem->version_num;
    $hrProperties->{file_dir}       = $dbItem->file_dir;
    $hrProperties->{file_name}      = $dbItem->file_name;
    $hrProperties->{orig_file_name} = $dbItem->orig_file_name;
    $hrProperties->{file_status}    = $dbItem->file_status;
    $hrProperties->{deleted}        = $dbItem->deleted;
    $hrProperties->{import_pid}     = $dbItem->import_pid;      # <-- this should probably be a job_id now-adays.
    $hrProperties->{records}        = $dbItem->records;
    $hrProperties->{units}          = $dbItem->units;
    $hrProperties->{revenue}        = $dbItem->revenue;
    $hrProperties->{currency_code}  = $dbItem->currency_code;   # !!! I wonder about this - Should we bother since we have mixed currencies?
    $hrProperties->{total_exceptions}      = $dbItem->total_exceptions;
    $hrProperties->{remaining_exceptions}  = $dbItem->remaining_exceptions;
    $hrProperties->{input_conversion_rate} = $dbItem->input_conversion_rate;
    $hrProperties->{input_revenue}         = $dbItem->input_revenue;
    $hrProperties->{notes}                 = $dbItem->notes;
    $hrProperties->{md5_sum}               = $dbItem->file_md5sum;

    $hrProperties->{date_start} = $dbItem->date_start;
    $hrProperties->{date_end}   = $dbItem->date_end;

    $hrProperties->{date_processed} = $dbItem->date_processed;
    $hrProperties->{date_created}   = $dbItem->date_created;

    $hrProperties->{date_finished} = $dbItem->date_finished;
    $hrProperties->{date_modified} = $dbItem->date_modified;

}

sub _initProperties {
    my ( $self, $hrProperties ) = @_;

    # JPK - Because this is a 'read only' object, I am only going to use
    # 'Scalar' fields when I want to get some formatting (dates, integers, etc).
    #
    $self->{POSFileID}    = $hrProperties->{pos_file_id};
    $self->{ServiceID}    = $hrProperties->{service_id};
    $self->{TypeID}       = $hrProperties->{type_id};
    $self->{VersionNum}   = $hrProperties->{version_num};
    $self->{FileDir}      = $hrProperties->{file_dir};
    $self->{FileName}     = $hrProperties->{file_name};
    $self->{OrigFileName} = $hrProperties->{orig_file_name};
    $self->{FileStatus}   = $hrProperties->{file_status};
    $self->{Deleted}      = $hrProperties->{deleted};
    $self->{Records}      = Common::FormObject::Scalar::Integer->new( value => $hrProperties->{records} );
    $self->{Units}        = Common::FormObject::Scalar::Integer->new( value => $hrProperties->{units} );
    $self->{Revenue} =
      Common::FormObject::Scalar::Money->new( value => $hrProperties->{revenue}, currencyCode => $hrProperties->{currency_code} );
    $self->{CurrencyCode}        = $hrProperties->{currency_code};
    $self->{InputConversionRate} = $hrProperties->{input_conversion_rate};
    $self->{InputRevenue}        = Common::FormObject::Scalar::Money->new( value => $hrProperties->{input_revenue} );
    $self->{Notes}               = $hrProperties->{notes};
    $self->{MD5Sum}              = $hrProperties->{md5_sum};

    $self->{TotalExceptions}     = Common::FormObject::Scalar::Integer->new( value => $hrProperties->{total_exceptions} );
    $self->{RemainingExceptions} = Common::FormObject::Scalar::Integer->new( value => $hrProperties->{remaining_exceptions} );

    $self->{DateStart} = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_start} );
    $self->{DateEnd}   = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_end} );

    $self->{DateProcessed} = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_processed} );
    $self->{DateFinished}  = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_finished} );

    # !!! Going to use the 'Date' class, rather than DateTime - We don't care to display the time...
    #
    $self->{DateCreated}  = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_created} );
    $self->{DateModified} = Common::FormObject::Scalar::Date->new( value => $hrProperties->{date_modified} );

    if ( $hrProperties->{service_id} ) {
        my $service = BookPub::Tracker::Service::ServiceWithConfig->new( serviceID => $hrProperties->{service_id} );
        if ($service) {
            $self->{Service} = $service;

            # !!! Templates should just look for the service object, but for now we'll be redundant.
            #
            $self->{ServiceName} = $self->{Service}->ServiceName();

            #		$self->{ServiceName} = Tracker::Service::GetServiceName(service_id => $hrProperties->{service_id});
        }
    }
    $self->{ServiceName} ||= 'Unknown';

    $self->{FileStatusText} = BookPub::DB::Item::POSFile->StatusCodeToText( $hrProperties->{file_status} );

    if ( $hrProperties->{file_id} && ( $self->{LocaleCurrency} ne $hrProperties->{currency_code} ) ) {
        my $revenue    = 0;
        my $inputRates = BookPub::DB::Item::InputConversionRate->GetAllByFileID( $hrProperties->{file_id} );
        if ( $inputRates->size ) {
            while ( my $item = $inputRates->next() ) {
                $revenue += $item->revenue * $item->conversion_rate;
            }
        }

        $self->{RevenueBase} = Common::FormObject::Scalar::Money->new( value => $revenue, currencyCode => $self->{LocaleCurrency} );
    }

    return $self;
}

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

    my $dbItem;

    if ( $self->POSFileID ) {
        $dbItem = BookPub::DB::Item::POSFile->Lookup( pos_file_id => $self->POSFileID() );
        die "POS File ID " . $self->POSFileID . " not found" unless ($dbItem);
    }

    if ( !$dbItem ) {
        $dbItem = BookPub::DB::Item::POSFile->Create();
        $dbItem->date_created( Common::DB::Item::kDateTimeNow() );
    }

    $dbItem->service_id( $self->ServiceID );
    $dbItem->type_id( $self->TypeID );
    $dbItem->version_num( $self->VersionNum );
    $dbItem->file_dir( $self->FileDir );
    $dbItem->file_name( $self->FileName );
    $dbItem->orig_file_name( $self->OrigFileName );
    $dbItem->file_status( $self->FileStatus );
    $dbItem->deleted( $self->Deleted );
    $dbItem->records( $self->Records );
    $dbItem->units( $self->Units );
    $dbItem->revenue( $self->Revenue );
    $dbItem->currency_code( $self->CurrencyCode );
    $dbItem->input_conversion_rate( $self->InputConversionRate );
    $dbItem->input_revenue( $self->InputRevenue );
    $dbItem->notes( $self->Notes );
    $dbItem->file_md5sum( $self->MD5Sum );
    $dbItem->date_finished( $self->DateFinished );
    $dbItem->date_start( $self->DateStart );
    $dbItem->date_end( $self->DateEnd );
    $dbItem->total_exceptions( $self->TotalExceptions );
    $dbItem->remaining_exceptions( $self->RemainingExceptions );

    $dbItem->save();
    $self->_init( dbItem => $dbItem );

    return 1;
}

sub updateSummary {
    my ( $self, %args ) = @_;

    my $fileID = $self->POSFileID;
    return unless $fileID;

    my ( $units, $records, $revenue, $totalExceptions, $remainingExceptions, $currencyCode ) =
      BookPub::DB::Item::POSSale->CalculateFileSummaryInfo( $self->POSFileID );

    $self->Units($units);
    $self->Records($records);
    $self->Revenue($revenue);
    $self->RemainingExceptions($remainingExceptions);
    $self->TotalExceptions($totalExceptions) if ( !defined $self->TotalExceptions );
    $self->CurrencyCode($currencyCode);
    $self->InputConversionRate(1) if ( $self->{LocaleCurrency} && $currencyCode && $self->{LocaleCurrency} ne $currencyCode );
}

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

    if ( BookPub::DB::Item::POSFile::STATUS_CLOSED == $self->FileStatus() ) {
        die RSApache::Message->new( type => 'error', code => 'file_not_deleted' );
    }

    # Delete the file on the server, and any associated sales (if there is a file)
    #
    my $fileID = $self->POSFileID();

    if ($fileID) {
        my $file = $self->FileDir . $self->FileName;
        if ( !-e $file
            || ( -f $file && unlink($file) ) ) {
            BookPub::DB::Item::POSSale->DeleteByPOSFileID($fileID);
        }
    }

    # Delete the database record.
    #
    if ( $self->{_dbItem} ) {
        $self->{_dbItem}->delete();
    }

}

sub finishImport {
    my ($self) = @_;
    my $today = Common::Util::today_and_now();

    my $newFile;
    $self->recalculateRemainingExceptions();

    my $client = Common::Client::Current();

    if ( $self->RemainingExceptions > 0
        || ( $client->isClientType(Common::Client::kSalePriceValidation) && $self->PriceExceptionsUnapproved > 0 ) ) {

        # split
        $newFile = $self->split();
        if ( !$newFile ) {
            die "Split failed";
        }
        $self->updateSummary();
    }

    $self->FileStatus(BookPub::DB::Item::POSFile::STATUS_CLOSED);
    $self->DateFinished($today);

    $self->save();

    return $newFile;
}

sub getDateRange {
    my $self = shift;

    return BookPub::DB::Item::POSSale->GetDateRangeByFileID( $self->POSFileID );
}

sub getCurrencyList {
    my $self = shift;

    my @currencyList = ();
    my $inputRates   = BookPub::DB::Item::InputConversionRate->GetAllByFileID( $self->POSFileID );
    if ( $inputRates->size ) {
        while ( my $item = $inputRates->next() ) {
            my %entry = (
                CurrencyCode  => $item->currency,
                NativeRevenue => Common::Client::Current()->Locale()->formatMoney( $item->revenue, $item->currency ),
                RawRevenue    => $item->revenue,
                RevenueBase   => Common::FormObject::Scalar::Money->new(
                    value        => $item->revenue * $item->conversion_rate,
                    currencyCode => $self->{LocaleCurrency}
                ),
            );
            if ( $item->conversion_rate > 0 ) {
                my $rate = $item->conversion_rate;
                $entry{Rate} = Common::Client::Current()->Locale()->formatNumber( sprintf( "%0.2f", $rate ) ),
                  $entry{NativeRate} = 1 / $rate,
                  ;
            }
            push( @currencyList, \%entry );
        }
    } else {
        my $currencyRev = BookPub::DB::Item::POSSale->GetCurrencyRevenueSummaryByFileID( fileID => $self->POSFileID );
        foreach my $currency ( sort keys %$currencyRev ) {
            my %entry = (
                CurrencyCode  => $currency,
                NativeRevenue => Common::Client::Current()->Locale()->formatMoney( $currencyRev->{$currency}, $currency ),
                RawRevenue    => $currencyRev->{$currency},
            );

            # in the case of multiple currency, 1 could be their preferred
            if ( $currency eq $self->{LocaleCurrency} && $self->InputConversionRate ) {
                $entry{Rate}        = 1;
                $entry{NativeRate}  = 1;
                $entry{RevenueBase} = Common::FormObject::Scalar::Money->new(
                    value        => sprintf( "%0.2f", $entry{RawRevenue} ),
                    currencyCode => $self->{LocaleCurrency}
                  ),
                  ;
            }
            push( @currencyList, \%entry );
        }
    }

    return \@currencyList;
}

sub setStatusProcessing {
    shift->setStatus(BookPub::DB::Item::POSFile::STATUS_PROCESSING);
}

sub setStatusFetching {
    shift->setStatus(BookPub::DB::Item::POSFile::STATUS_FETCHING);
}

sub setStatusFetchError {
    shift->setStatus(BookPub::DB::Item::POSFile::STATUS_FETCH_ERROR);
}

sub setStatusOpen {
    shift->setStatus(BookPub::DB::Item::POSFile::STATUS_OPEN);
}

sub setStatusClosed {
    shift->setStatus(BookPub::DB::Item::POSFile::STATUS_CLOSED);
}

sub setStatus {
    my $self   = shift;
    my $status = shift;

    assert($status);

    $self->FileStatus($status);
    $self->save();
}

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

    my $remainingExceptions = BookPub::DB::Item::POSSale->CountRemainingExceptions( $self->POSFileID );
    $self->RemainingExceptions($remainingExceptions);
}

sub setRetry {
    my $self = shift;

    assert( $self->POSFileID );

    BookPub::DB::Item::POSFile->unsetFile( pos_file_id => $self->POSFileID );
    $self->_init( posFileID => $self->POSFileID );

    $self->setStatusFetching();

}

1;
