#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
package API::Command;

#
# This class is intended to be a base class for all the API commands
#

use strict;
use warnings;

use Data::Dumper;
use JSON;
use utf8;
use CGI::Cookie;
use Apache2::Const qw(OK FORBIDDEN HTTP_OK HTTP_UNAUTHORIZED HTTP_NOT_FOUND HTTP_INTERNAL_SERVER_ERROR);
use URI::Escape;

use lib '/app/tools/common/lib';
use RSApache::Command::SessionedCommand;
use Common::DB::Item::PayeeType;
use Common::Log;

use lib '/app/tools/rps/lib';
use RPS::DB::Item::ArtistPayee;
use RPS::DB::Item::ArtistPayeeStatementRecipient;
use RPS::DB::Item::Publisher;
use RPS::DB::Item::PublisherStatementRecipient;

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

use base 'RSApache::Command::SessionedCommand';

use constant PAYEE_TYPE_MAP => {
    Common::DB::Item::PayeeType::kPayeeTypeArtist => {
        type      => 'artist_payee',
        run_type  => 'artist',
        payee     => 'RPS::DB::Item::ArtistPayee',
        recipient => 'RPS::DB::Item::ArtistPayeeStatementRecipient',
    },
    Common::DB::Item::PayeeType::kPayeeTypePublisherUS => {
        type      => 'publisher',
        run_type  => 'publisher',
        payee     => 'RPS::DB::Item::Publisher',
        recipient => 'RPS::DB::Item::PublisherStatementRecipient',
    },
    Common::DB::Item::PayeeType::kPayeeTypePublisherCA => {
        type      => 'ca_publisher',
        run_type  => 'ca_publisher',
        payee     => 'RPS::DB::Item::CAPublisher',
        recipient => 'RPS::DB::Item::CAPublisherStatementRecipient',
    },
};

sub app { return 'api'; }

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

    my $cgi = $self->getCGI();

    my $r = Apache->request();
    my $contentType = $r->header_in('Content-type') || '';    # valid for POST or PUT
    $self->{ContentType} = "$contentType";

    # Create a local cache of the parameters passed in.  This should cover all
    # GET, POST, PUT parameters regardless of content type.  When implementing
    # a command, all that is needed is to use getInputParam(name) to retrieve
    # an input parameter.
    my $params = $self->getParams();

    my %cache = ();

    foreach my $p ( keys %$params ) {

        if ( $p eq 'POSTDATA' ) {

            # Retrieve input parameters from a JSON post
            next if( !defined $params->{$p} || $params->{$p} eq '{}' );
            eval {
                my $json = JSON->new->utf8(0)->decode( $params->{$p} );
                foreach my $k ( keys %$json ) {
                    my $v = $json->{$k};
                    $cache{$k} = $v;
                }
                1;
            } or do {
                $self->{_bad_json} = 1;
            }

        } elsif ( $p eq 'PUTDATA' ) {

            # Retrieve input parameters from a JSON put
            next if( !defined $params->{$p} || $params->{$p} eq '{}' );
            eval {
                my $json = JSON->new->utf8(0)->decode( $params->{$p} );
                foreach my $k ( keys %$json ) {
                    my $v = $json->{$k};
                    $cache{$k} = $v;
                }
                1;
            } or do {
                $self->{_bad_json} = 1;
            }
        } else {

            # Retrieve query parameter
            $cache{$p} = $params->{$p};
        }
    }

    $self->{ParamCache} = \%cache;

    my $c_type = ( '' eq $contentType ) ? '' : "'" . $contentType . "' ";

    Common::Log::Print( "INIT API COMMAND: "
          . $cgi->request_method() . " "
          . $self->app() . '/'
          . $self->area() . '/'
          . $self->cmd() . " "
          . $c_type
          . " PARAMS: "
          . $self->getParamsAsString( 'password' => 1 ) ) if( $self->cmd() && $self->cmd() ne 'hello' );

    return $self;
}

sub getInputParam {
    my ( $self, $name ) = @_;
    return ( exists $self->{ParamCache}->{$name} ) ? $self->{ParamCache}->{$name} : undef;
}

sub getParamsAsString {
    my ( $self, %skip ) = @_;

    my @pairs;
    my $params = $self->{ParamCache};

    while ( my ( $key, $value ) = each %$params ) {
        if ( $skip{$key} ) {
            push @pairs, join( '=', $key, "XXXX" );
        } else {
            push @pairs, join( '=', $key, URI::Escape::uri_escape_utf8($value) );
        }
    }

    return join( '&', @pairs );
}

sub createResponse {
    my ( $self, %args ) = @_;
    my $statusCode = $args{status};
    my $data       = $args{data};

    my $response = API::Response->new($data);
    $response->{status_code} = $statusCode;
    return $response;
}

sub getRouteParam {
    my ( $self, $name ) = @_;
    return if ( !$self->{routevars} );

    my $vars = $self->{routevars};

    foreach my $k ( keys %{$vars} ) {
        my $v = $vars->{$k};

        return uri_unescape($v) if ( $name =~ /$k/i );
    }
}

# Some command routes include clientID information.  This method checks
# if a userID has a valid payee portal relation with a clientID.
sub canAccess {
    my ( $self, $userID, $clientID ) = @_;
    my $o = API::DB::Item::PortalPayee->Lookup( user_id => $userID, client_id => $clientID );
    if ($o) {
        return 1 if ( $o->date_confirmed );
    }
    return undef;
}

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

    if ( exists $self->{_bad_json} && $self->{_bad_json} == 1 ) {
        return $self->createResponse(
            status => HTTP_INTERNAL_SERVER_ERROR,
            data   => {}
        );
    }

    unless ( API::Session->IsValid() ) {
        return $self->createResponse(
            status => HTTP_UNAUTHORIZED,
            data   => {}
        );
    }

    my $sessionID = API::Session->GetSessionID();
    my $session   = API::DB::Item::PortalSession->Lookup( id => $sessionID );
    my $userID    = $session->user_id;
    $self->{userID} = $userID;

    return;
}


sub isSupportedPayeeType {
    my ($self, $payeeType) = @_;
    # returns payee_type_id if the type is supported,
    # otherwise returns undef

    return grep { $_ == $payeeType } grep {$_} keys %{ +PAYEE_TYPE_MAP };
}

sub getAllowedRunTypes {
    my $self = shift;
    # returns a list of allowed run types

    return map { PAYEE_TYPE_MAP->{$_}{run_type} } keys %{ +PAYEE_TYPE_MAP };
}

sub getPayeeTypeByRunType {
    my ($self, $runType) = @_;
    # returns payee_type_id for the associated runType

    foreach my $runTypeID ( keys %{ +PAYEE_TYPE_MAP }  ) {
        next if PAYEE_TYPE_MAP->{$runTypeID}{run_type} ne $runType;
        return $runTypeID;
    }

    return 0;
}

sub getPayeeOrRecipientByType {
    my ($self, $payeeType, $payeeID, $recipientID) = @_;
    # returns a primary payee object or an additional
    # payee statement recipient object for the desired payee type.
    # returns undef if not found

    return unless $self->isSupportedPayeeType($payeeType);

    my $payeeClass     = PAYEE_TYPE_MAP->{$payeeType}{payee};
    my $recipientClass = PAYEE_TYPE_MAP->{$payeeType}{recipient};
    my $payeeKeyField  = PAYEE_TYPE_MAP->{$payeeType}{type} . '_id';

    my $oPayee = !$recipientID
        ? $payeeClass->Lookup( $payeeKeyField => $payeeID )
        : $recipientClass->Lookup( $payeeKeyField => $payeeID, statement_recipient_id => $recipientID );

    return $oPayee;
}


1;
