#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RPS::ArtistPayee::ArtistPayee;

use strict;
use warnings;

use lib '/app/tools/common/lib';
use Common::DB::Item::PayeeType;

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

use lib '/app/tools/rps/lib';
use RPS::DB::Item::ArtistPayee;
use RPS::DB::Item::ArtistPayeeAccount;
use RPS::DB::Item::ArtistPayeeStatementRecipient;
use RPS::DB::Item::Payor;
use RPS::DB::Item::ClientOptions;

use RPS::Finance::Account;
use RPS::StateList;
use RPS::Portal::Token;
use RPS::ArtistPayee::ArtistPayeeSimple;
use RPS::ArtistPayee::ArtistPayeeAccount;
use RPS::Payee::ArtistPayeeStatementRecipient;
use RPS::Portal::SendInvite;
use base 'RPS::ArtistPayee::ArtistPayeeSimple';

my $DISTRIBUTION_MANUAL = RPS::DB::Item::ArtistPayee::kStatementDistributionManual;
my $DISTRIBUTION_ONLINE = RPS::DB::Item::ArtistPayee::kStatementDistributionOnline;
my $PAYEE_TYPE          = Common::DB::Item::PayeeType::kPayeeTypeArtist;


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

    # First, call the inherited method to get it's fields.
    #
    $self->SUPER::_propertiesFromDBItem( $hrProperties, $dbItem );

    # $hrProperties->{artist_payee_id}    = $dbItem->artist_payee_id;
    # $hrProperties->{name}               = $dbItem->name;
    $hrProperties->{street_address}   = $dbItem->street_address;
    $hrProperties->{street_address_2} = $dbItem->street_address_2;
    $hrProperties->{street_address_3} = $dbItem->street_address_3;
    $hrProperties->{city}             = $dbItem->city;
    $hrProperties->{state_province}   = $dbItem->state_province;
    $hrProperties->{country_code}     = $dbItem->country_code;
    $hrProperties->{postal_code}      = $dbItem->postal_code;
    $hrProperties->{email}            = $dbItem->email;
    $hrProperties->{phone_number}     = $dbItem->phone_number;
    $hrProperties->{fax_number}       = $dbItem->fax_number;
    $hrProperties->{tax_id}           = $dbItem->tax_id;
    $hrProperties->{cycle_code}       = $dbItem->cycle_code;

    #	$hrProperties->{min_payment}        = $dbItem->min_payment;
    $hrProperties->{begin_month_code} = $dbItem->begin_month_code;
    $hrProperties->{comments}         = $dbItem->comments;

    #    $hrProperties->{finance_account_id} = $dbItem->finance_account_id;
    #    $hrProperties->{opening_balance}    = $dbItem->opening_balance;

    $hrProperties->{client_payee_id} = $dbItem->client_payee_id;
    $hrProperties->{status}          = $dbItem->status;

    $hrProperties->{date_created}   = $dbItem->date_created;
    $hrProperties->{date_modified}  = $dbItem->date_modified;
    $hrProperties->{created_by}     = $dbItem->created_by;
    $hrProperties->{modified_by}    = $dbItem->modified_by;
    $hrProperties->{distribution}   = $dbItem->distribution;
    $hrProperties->{date_invited}   = $dbItem->date_invited;
    $hrProperties->{date_confirmed} = $dbItem->date_confirmed;

    $hrProperties->{abacus_contract_id} = $dbItem->abacus_contract_id;
    $hrProperties->{abacus_account_id}  = $dbItem->abacus_account_id;
}

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

    # Call the inherited method first, to let it get the first crack at this.
    $self->SUPER::_initProperties($props);

    $self->{StreetAddress}    = Common::FormObject::Scalar::String->new( value => $props->{street_address} );
    $self->{StreetAddress2}   = Common::FormObject::Scalar::String->new( value => $props->{street_address_2} );
    $self->{StreetAddress3}   = Common::FormObject::Scalar::String->new( value => $props->{street_address_3} );
    $self->{City}             = Common::FormObject::Scalar::String->new( value => $props->{city}, maxlength => 80 );
    $self->{StateProvince}    = Common::FormObject::Scalar::String->new( value => $props->{state_province}, maxlength => 80 );
    $self->{CountryCode}      = Common::FormObject::Scalar::String->new( value => $props->{country_code}, maxlength => 2 );
    $self->{PostalCode}       = Common::FormObject::Scalar::String->new( value => $props->{postal_code}, maxlength => 15 );
    $self->{Email}            = Common::FormObject::Scalar::EmailAddress->new( value => $props->{email}, maxlength => 80 );
    $self->{OrigEmail}        = Common::FormObject::Scalar::EmailAddress->new( value => $props->{email}, maxlength => 80 );
    $self->{PhoneNumber}      = Common::FormObject::Scalar::String->new( value => $props->{phone_number}, maxlength => 30 );
    $self->{FaxNumber}        = Common::FormObject::Scalar::String->new( value => $props->{fax_number},   maxlength => 30 );
    $self->{TaxID}            = Common::FormObject::Scalar::String->new( value => $props->{tax_id},       maxlength => 30 );
    $self->{CycleCode}        = Common::FormObject::Scalar->new( value => $props->{cycle_code} );

    $self->{BeginMonthCode}   = Common::FormObject::Scalar->new( value => $props->{begin_month_code} );
    $self->{Comments}         = Common::FormObject::Scalar::String->new( value => $props->{comments}, maxlength => 0 );
    $self->{ClientPayeeID}    = Common::FormObject::Scalar::String->new( value => $props->{client_payee_id} );
    $self->{Status}           = Common::FormObject::Scalar->new( value => $props->{status} );

    $self->{CreatedDate}      = Common::FormObject::Scalar::DateTime->new( value => $props->{date_created},  readOnly => 1 );
    $self->{ModifiedDate}     = Common::FormObject::Scalar::DateTime->new( value => $props->{date_modified}, readOnly => 1 );
    $self->{CreatedBy}        = Common::FormObject::Scalar::UserName->new( value => $props->{created_by},    readOnly => 1 );
    $self->{ModifiedBy}       = Common::FormObject::Scalar::UserName->new( value => $props->{modified_by},   readOnly => 1 );

    $self->{Distribution}     = Common::FormObject::Scalar->new( value => $props->{distribution} );
    $self->{OrigDistribution} = Common::FormObject::Scalar->new( value => $props->{distribution} );

    $self->{DateInvited}      = Common::FormObject::Scalar::DateTime->new( value => $props->{date_invited},   readOnly => 1 );
    $self->{DateConfirmed}    = Common::FormObject::Scalar::DateTime->new( value => $props->{date_confirmed}, readOnly => 1 );
    $self->{ShowAbacusData}   = RPS::DB::Item::ClientOptions->Get( 'show_abacus_data' );
    $self->{AbacusContractID} = Common::FormObject::Scalar->new( value => $props->{abacus_contract_id} );
    $self->{AbacusAccountID}  = Common::FormObject::Scalar->new( value => $props->{abacus_account_id} );

    if ( $props->{artist_payee_id} ) {
        my $relatedArtistPayeeItems = (
               RPS::DB::Item::ArtistPayee->HasArtistContracts($props->{artist_payee_id})
            || RPS::DB::Item::ArtistPayee->IsNotManual($props->{artist_payee_id})
            || RPS::DB::Item::ArtistPayee->HasPendingTransactions($props->{artist_payee_id})
            || RPS::DB::Item::ArtistPayee->HasTransactionHistory($props->{artist_payee_id})
            || RPS::DB::Item::ArtistPayee->HasRunActivity($props->{artist_payee_id})) ? 1 : 0;
        $self->{RelatedArtistPayeeItems} = Common::FormObject::Scalar->new( value => $relatedArtistPayeeItems );
    }

    # A Payee Portal stuff
    $self->_canInvitePayee($props);
    $self->_calcStatementRecipients($props);

    return $self;
}

sub _initSubs {
    my ( $self, $props ) = @_;

    if ( $self->loadSubs() ) {
        # Account information (including current balance) will be held in a seperate class.
        my $artistPayeeID = $self->ArtistPayeeID();
        if ($artistPayeeID) {
            my $accounts = RPS::DB::Item::ArtistPayeeAccount->GetByArtistPayeeID($artistPayeeID);
            while ( my $account = $accounts->next() ) {
                push @{ $self->{Account} }, RPS::Payee::ArtistPayeeAccount->new( _dbItem => $account, readOnly => 1 );
            }
        }
    }
}

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

    my $valid = $self->SUPER::validate();

    # Email required if the payee is set for online statement distribution
    if ( ( $self->DateInvited() || $self->Distribution() == $DISTRIBUTION_ONLINE )
        && $self->Email() eq '' )
    {
        $self->{Email}->setError(Common::FormObject::kErrRequiredForDistribution);
        $valid = 0;
    }

    if ( $self->StateProvince() && $self->CountryCode() ) {
        if ( $self->CountryCode() eq 'US' ) {
            if ( !RPS::StateList::IsValidState( $self->StateProvince() ) ) {
                $self->{StateProvince}->setError(Common::FormObject::kErrInvalidState);
                $valid = 0;
            }
        }
    }

    if ( 1 == RPS::DB::Item::ClientOptions->Get( 'show_abacus_data' ) ) {
        # optional, but must be numeric if present
        if ( '' ne $self->AbacusAccountID() ) {
            if ( $self->AbacusAccountID() !~ /^\d+$/ ) {
                $self->{AbacusAccountID}->setError(1);
                $self->{AbacusAccountID}->setErrorString('must be numeric');
                $valid = 0;
            } elsif( $self->AbacusAccountID() > 4294967295 ) {  # don't exceed max INT UNSIGNED
                $self->{AbacusAccountID}->setError(1);
                $self->{AbacusAccountID}->setErrorString('values greater than 4294967295 not allowed');
                $valid = 0;
            }
        }
        if ( '' ne $self->AbacusContractID() ) {
            if ( $self->AbacusContractID() !~ /^\d+$/ ) {
                $self->{AbacusContractID}->setError(1);
                $self->{AbacusContractID}->setErrorString('must be numeric');
                $valid = 0;
            } elsif( $self->AbacusContractID() > 4294967295 ) {  # don't exceed max INT UNSIGNED
                $self->{AbacusContractID}->setError(1);
                $self->{AbacusContractID}->setErrorString('values greater than 4294967295 not allowed');
                $valid = 0;
            }
        }
    }

    return $valid;
}

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

    my $dbObj = $self->ArtistPayeeID()
        ? RPS::DB::Item::ArtistPayee->Lookup( artist_payee_id => $self->ArtistPayeeID() )
        : RPS::DB::Item::ArtistPayee->Create();

    $dbObj->name( $self->Name() );
    $dbObj->street_address( $self->StreetAddress() );
    $dbObj->street_address_2( $self->StreetAddress2() );
    $dbObj->street_address_3( $self->StreetAddress3() );
    $dbObj->city( $self->City() );
    $dbObj->state_province( $self->StateProvince() );
    $dbObj->country_code( $self->CountryCode() );
    $dbObj->postal_code( $self->PostalCode() );
    $dbObj->email( $self->Email() );
    $dbObj->phone_number( $self->PhoneNumber() );
    $dbObj->fax_number( $self->FaxNumber() );
    $dbObj->tax_id( $self->TaxID() );
    $dbObj->cycle_code( $self->CycleCode() );
    $dbObj->begin_month_code( $self->BeginMonthCode() );
    $dbObj->comments( $self->Comments() );
    $dbObj->client_payee_id( $self->ClientPayeeID() );
    $dbObj->client_account_id( $self->ClientAccountID() );
    $dbObj->status( $self->Status() );
    $dbObj->abacus_contract_id( $self->AbacusContractID() );
    $dbObj->abacus_account_id( $self->AbacusAccountID() );


    # If we're going to send out an invite, clear the invite date.
    # It will be reset when the invite is sent out.
    my $sendInviteFlag = $self->_needToSendInvite();
    if ( $sendInviteFlag ) {
        $dbObj->date_invited(undef);
        $dbObj->date_confirmed(undef);
    }

    # Do not update if Invite had been confirmed or Payee has recipients.
    if ( !$dbObj->date_confirmed && !$self->{RecipientsAssigned} ) {
         $dbObj->distribution( $self->Distribution() );
    }

    # save db item
    $dbObj->save();


    my $payeeID = $dbObj->artist_payee_id;
    $self->_processArtistPayeeMinPayment(payee_id => $payeeID);
    $self->_sendInvite($payeeID) if $sendInviteFlag;

    # reload object
    $self->_init( _dbItem => $dbObj );
}


sub _sendInvite {
    my ($self, $payeeID) = @_;

    my $sent = RPS::Portal::SendInvite->_sendInvite(
        payee_id   => $payeeID,
        payee_type => $PAYEE_TYPE
    );
    if ( $sent ) {
        Common::Log::Print( "ArtistPayee.save: Sending invite to payeeID $payeeID" );
        return 1;
    }

    return 0;
}

sub _deleteInvite {
    my ($self, $email) = @_;

    my $oInvites = API::DB::Item::PortalInvite->GetByInvitee($email);
    while ( $oInvites->hasNext() ) {
        my $oInvite = $oInvites->next();
        if (   $oInvite->client_id == Common::RSApp::GetClientID()
            && $oInvite->payee_id == $self->ArtistPayeeID
            && $oInvite->payee_type == $PAYEE_TYPE ) {

            $oInvite->delete;
            return 1;
        }
    }

    return 0;
}

sub _deletePortalPayee {
    my ($self, $email) = @_;

    my $oPortalPayees = API::DB::Item::PortalPayee->GetByInvitee($email);
    while ( $oPortalPayees->hasNext() ) {
        my $oPortalPayee = $oPortalPayees->next();
        if (   $oPortalPayee->client_id == Common::RSApp::GetClientID()
            && $oPortalPayee->payee_id == $self->ArtistPayeeID
            && $oPortalPayee->payee_type == $PAYEE_TYPE ) {

            $oPortalPayee->delete;
            return 1;
        }
    }

    return 0;
}

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

    my $payee_id = $args{payee_id} || 0;
    (Common::Log::Print("Unable to set Artist Payee Min Payment due to missed payee ID") and return undef) unless $payee_id;

    my $payors = RPS::DB::Item::Payor->GetPayorsWithArtistPayeeMinPayment();

    while ($payors->hasNext()) {
        my $payor = $payors->next;
        $self->{ArtistPayeeAccount} = RPS::ArtistPayee::ArtistPayeeAccount->new( artistPayeeID => $payee_id, payorID => $payor->{payor_id} );
        $self->{ArtistPayeeAccount}->{MinPayment} = $payor->{artist_payee_min_payment};
        $self->{ArtistPayeeAccount}->save;
    }
}

sub _needToSendInvite {
	my $self = shift;

	my $inviteFlag = 0;

	# New Artist Payee
	if ( !$self->ArtistPayeeID ) {
		$inviteFlag = $self->Distribution() == $DISTRIBUTION_ONLINE ? 1 : 0;
	}

	# Existing Artist Payee
    # During a Artist Payee update, we'll send out an invite under the following conditions:
    #   - The distribution status has changed from manual to online; or
    #   - The email address has been changed
	if ( $self->Distribution() == $DISTRIBUTION_ONLINE ) {
        if ( $self->OrigDistribution() == $DISTRIBUTION_MANUAL ) {
            Common::Log::Print("ArtistPayee.save: payee update, distributioin status");
            $inviteFlag = 1;
        }

        if ( $self->Email() ne $self->OrigEmail() ) {
            $self->_deleteInvite( $self->OrigEmail() );
            $self->_deletePortalPayee( $self->OrigEmail() );
            Common::Log::Print("ArtistPayee.save: payee update, email changed");
            $inviteFlag = 1;
        }
	}

	if ( $self->Distribution() == $DISTRIBUTION_MANUAL ) {
		if ( $self->OrigDistribution() == $DISTRIBUTION_ONLINE ) {
			if ( !$self->_deleteInvite( $self->Email() ) ) {
				Common::Log::Print(
					"ArtistPayee.save: distribution changed from online to manual, but confirmed invite exists for old email");
			}
		}
	}

	return $inviteFlag;
}

sub _canInvitePayee {
    my ($self, $hProps) = @_;

    $self->{CanInvite} = 0;

    if ( $hProps->{date_invited} ) {
        $self->{CanInvite} = RPS::Portal::SendInvite::_checkDateInvited( $hProps->{date_invited} );
    }

    return $self->{CanInvite};
}

sub _calcStatementRecipients {
    my ($self, $hProps) = @_;

    $self->{RecipientsAssigned}  = 0;
    $self->{RecipientsConfirmed} = 0;
    return unless $hProps->{artist_payee_id};

    my $oRecipients = RPS::DB::Item::ArtistPayeeStatementRecipient->GetByArtistPayeeID( $hProps->{artist_payee_id} );
    while ( $oRecipients && $oRecipients->hasNext() ) {
        $self->{RecipientsConfirmed}++ if $oRecipients->next()->date_confirmed;
        $self->{RecipientsAssigned}++;
    }

    return 1;
}


1;
