#------------------------------------------------------------
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RPS::DB::Item::DynamicReport::Transactions::PublisherBase;

# This is the base class for the ArtistProducer and Label reports.

use strict;
use warnings;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';

use Common::Log;
use Common::DB::Item;
use Common::DB::ItemCollection;
use Common::Assert;

use RPS::DB::Item::FinanceTransaction;

use Data::Dumper;
use List::Compare;

use base 'RPS::DB::Item::DynamicReport';

use constant kDB => Common::DB::Item::kClientDB();

sub _config {
    my ($class) = @_;
    return {
        'publisher'          => {},
        'client_account_no'  => {},
        'publisher_id'       => {},
        'publisher_status'   => {},
        'publisher_type'     => {},
        'agent_name'         => {},
        'admin_name'         => {},
        'license_count'      => {},
        'payor_name'         => {},
        'payor_id'           => {},
        'client_payor_no'    => {},
        'payor_status'       => {},
        'default_payor'      => {},
        'min_payment'        => {},
        't_status'           => {},
        't_type'             => {},
        't_amount'           => {},
        't_date'             => {},
        't_check_no'         => {},
        't_memo'             => {},
        't_balance'          => {},
        't_date_created'     => {},
        't_date_modified'    => {},
        't_id'               => {},
        'finance_account_id' => {},
    };

}

sub ReportQuery {
    my ( $class, %args ) = @_;

    # Since we're possibly combinine the contents of two transaction tables,
    # we've got to build two queries and possibly union them.
    #

    # And... we also need to take the 'publisher_indirect_balance_account' into account for
    # pending transactions that will end up being reported via an admin or agent.
    # I think that's going to be another set of queries unioned together.
    #

    my @queries;
    push @queries, $class->_BuildQuery( 'pending_transaction', %args )
      if ( $args{showUnprocessed} );

    push @queries, $class->_BuildQuery( 'finance_transaction', %args )
      if ( $args{showProcessed} );

    my @orderBy;
    $class->_BuildOrderBy( \@orderBy, %args );

    my $sql;
    if ( scalar @queries > 1 ) {
        $sql = join( ' UNION ', @queries );
        $sql = "SELECT * FROM ( $sql ) as subq ";
    } else {
        $sql = $queries[0];
    }

    if ( scalar @orderBy ) {
        $sql .= "\nORDER BY " . join( ',', @orderBy );
    }

    return $class->GetAll($sql);
}

sub _BuildQuery {
    my ( $class, $tType, %args ) = @_;

    return $class->_BuildQueryRegular( $tType, %args ) . ' UNION ' . $class->_BuildQueryIndirect( $tType, %args );
}

sub _BuildQueryRegular {
    my ( $class, $tType, %args ) = @_;

    my @select;
    $class->_BuildSelect( \@select, $tType, %args );

    my @joins;
    $class->_BuildJoins( \@joins, $tType, %args );

    my @groupBy;
    $class->_BuildGroupBy( \@groupBy, $tType, %args );

    my @where;
    $class->_BuildWhere( \@where, $tType, %args );

    my @from;
    $class->_BuildFrom( \@from, $tType, %args );

    my $sql;

    $sql .= 'SELECT ' . join( ",", @select ) . " FROM " . join( ',', @from );

    if ( scalar @joins ) {
        $sql .= " " . join( " ", @joins );
    }

    if ( scalar @where ) {
        $sql .= " WHERE " . join( " AND ", @where );
    }

    if ( scalar @groupBy ) {
        $sql .= " GROUP BY " . join( ',', @groupBy );
    }

    return $sql;
}

sub _BuildQueryIndirect {
    my ( $class, $tType, %args ) = @_;

    my @select;
    $class->_BuildSelectIndirect( \@select, $tType, %args );

    my @joins;
    $class->_BuildJoinsIndirect( \@joins, $tType, %args );

    my @groupBy;
    $class->_BuildGroupBy( \@groupBy, $tType, %args );

    my @where;
    $class->_BuildWhere( \@where, $tType, %args );

    # Both the 'direct' and 'indirect' queries start with the same tables (the actual transaction table)
    #
    my @from;
    $class->_BuildFrom( \@from, $tType, %args );

    my $sql;

    $sql .= 'SELECT ' . join( ",", @select ) . " FROM " . join( ',', @from );

    if ( scalar @joins ) {
        $sql .= " " . join( " ", @joins );
    }

    if ( scalar @where ) {
        $sql .= " WHERE " . join( " AND ", @where );
    }

    if ( scalar @groupBy ) {
        $sql .= " GROUP BY " . join( ',', @groupBy );
    }

    return $sql;
}

sub _BuildFrom {
    my ( $class, $from, $tType, %args ) = @_;

    push @$from, $tType;
}

sub _BuildSelect {
    my ( $class, $select, $t, %args ) = @_;

    return $class->_BuildSelectActual( $select, $t, $class->_publisher(), $class->_publisherAccount() );
}

sub _BuildSelectIndirect {
    my ( $class, $select, $t, %args ) = @_;

    # Passing '0' for the publisher account means that we don't care about any of those fields.
    # Right now we just look there for 'min_payment', which the indirect balance account doesn't have.
    #
    return $class->_BuildSelectActual( $select, $t, $class->_publisher(), 0 );
}

sub _BuildSelectActual {
    my ( $class, $select, $t, $publisher, $publisherAccount ) = @_;

    push @$select, (
        "$publisher.publisher_name as publisher",
        "$publisher.client_account_id as client_account_no",
        "$publisher.$publisher" . '_id as publisher_id',
        "IF( $publisher.status = 0, 'inactive', "
          . "IF( $publisher.status = 1, 'active', "
          . "IF( $publisher.status = 2, 'on hold', 'unknown' ) ) ) AS publisher_status",

        "IF( $publisher.is_agency = 1, 'agent', " . "IF( $publisher.is_admin = 1, 'admin', 'standard' ) ) as publisher_type",

        'payor.name as payor_name',
        'payor.payor_id as payor_id',
        "IF( payor.inactive, 'inactive', 'active' ) as payor_status",

        'payor.client_payor_id as client_payor_no',
        "IF( payor.is_default, 'yes', 'no' ) as default_payor",
        $publisherAccount
        ? "$publisherAccount.min_payment as min_payment"
        : "IF( $publisher.agent_id > 0, 'See Agent', IF( $publisher.admin_id > 0, 'See Admin', '')) as min_payment",
        ( $t eq 'finance_transaction' ? "'Processed'" : "'Pending'" ) . " as t_status",

        $class->_transactionTypeSQL($t) . ' as t_type',
        "$t.amount as t_amount",
        "$t.transaction_date as t_date",
        "$t.check_number as t_check_no",
        "$t.memo as t_memo",
        ( $t eq 'finance_transaction' ? "$t.balance" : "''" ) . " as t_balance",
        "$t.date_modified as t_date_created",
        "$t.date as t_date_modified",
        "$t." . $t . "_id as t_id",
        "$t.finance_account_id as finance_account_id",
        "IF( $publisher.agent_id, agent.publisher_name, '') as agent_name",
        "IF( $publisher.admin_id, admin.publisher_name, '') as admin_name",
    );

    # This bit of wackiness is to get a non-numeric field to sort numerically if it just contains digits, and
    # alphabetically otherwise.
    #
    push @$select, "IF($publisher.client_account_id REGEXP '^[[:digit:]]+" . '$'
      . "',LPAD($publisher.client_account_id,32,'0'),$publisher.client_account_id) as sort2";

    # JPK - Note the wacky mapping of dates.  That's just the way it is, sadly.
}

sub _transactionTypeSQL {
    my ( $class, $t ) = @_;

    return
        " IF( $t.type_code = "
      . RPS::DB::Item::FinanceTransaction::kTypeOpeningBalance
      . ", 'opening balance', "
      . " IF( $t.type_code = "
      . RPS::DB::Item::FinanceTransaction::kTypeAdjustment
      . ", 'adjustment', "
      . " IF( $t.type_code = "
      . RPS::DB::Item::FinanceTransaction::kTypeAdvance
      . ", 'advance', "
      . " IF( $t.type_code = "
      . RPS::DB::Item::FinanceTransaction::kTypePayment
      . ", 'payment', "
      . " IF( $t.type_code = "
      . RPS::DB::Item::FinanceTransaction::kTypeClosingBalance
      . ", 'closing balance', "
      . " IF( $t.type_code IS NULL, NULL, "
      . " 'unknown' ) ) ) ) ) ) ";
}

sub _BuildJoins {
    my ( $class, $joins, $t, %args ) = @_;

    push @$joins, 'JOIN ' . $class->_publisherAccount() . ' USING (finance_account_id)';
    push @$joins, 'JOIN ' . $class->_publisher() . ' USING (' . $class->_publisher() . '_id)';
    push @$joins, 'JOIN payor USING (payor_id)';
    push @$joins,
      'LEFT JOIN ' . $class->_publisher() . ' AS agent ON (' . $class->_publisher() . '.agent_id = agent.' . $class->_publisher() . '_id)';
    push @$joins,
      'LEFT JOIN ' . $class->_publisher() . ' AS admin ON (' . $class->_publisher() . '.admin_id = admin.' . $class->_publisher() . '_id)';
}

sub _BuildJoinsIndirect {
    my ( $class, $joins, $t, %args ) = @_;

    push @$joins, 'JOIN ' . $class->_publisherAccountIndirect() . ' USING (finance_account_id)';
    push @$joins, 'JOIN ' . $class->_publisher() . ' USING (' . $class->_publisher() . '_id)';
    push @$joins, 'JOIN payor USING (payor_id)';
    push @$joins,
      'LEFT JOIN ' . $class->_publisher() . ' AS agent ON (' . $class->_publisher() . '.agent_id = agent.' . $class->_publisher() . '_id)';
    push @$joins,
      'LEFT JOIN ' . $class->_publisher() . ' AS admin ON (' . $class->_publisher() . '.admin_id = admin.' . $class->_publisher() . '_id)';
}

sub _BuildGroupBy {
    my ( $class, $groupBy, $t, %args ) = @_;
}

sub _BuildWhere {
    my ( $class, $where, $t, %args ) = @_;

    my $dateType  = $args{dateType};
    my $startDate = $args{startDate};
    my $endDate   = $args{endDate};

    if ($dateType) {

        # Need to map the dateType to the actual field names.
        #
        my $dateField;
        if ( 'date_modified' eq $dateType ) {
            $dateField = 'date';
        } elsif ( 'date_created' eq $dateType ) {
            $dateField = 'date_modified';
        } else {
            die "ERROR : Invalid dateType $dateType";
        }

        if ($startDate) {
            push @$where, "TO_DAYS($t.$dateField) >= TO_DAYS(" . $class->quote($startDate) . ")";
        }
        if ($endDate) {
            push @$where, "TO_DAYS($t. $dateField) <= TO_DAYS(" . $class->quote($endDate) . ")";
        }
    }
}

sub _BuildOrderBy {
    my ( $class, $orderBy, %args ) = @_;
    push @$orderBy, "publisher";

    #    push @$orderBy, "client_account_no";
    push @$orderBy, "sort2";
    push @$orderBy, "finance_account_id";
    push @$orderBy, "payor_name";
    push @$orderBy, "t_status";
    push @$orderBy, "t_date_created";
}

1;
